Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> Node parameterTypeNode = parameterTypes.getFirstChild();
for (Node astParameter : astParameters.children()) {
Var var = functionScope.getVar(astParameter.getString());
Preconditions.checkNotNull(var);
if (var.isTypeInferred() &&
var.getType() == unknownType) {
JSType newType = null;
if (iifeArgumentNode != null) {
newType = iifeArgumentNode.getJSType();
} else if (parameterTypeNode != null) {
newType = parameterTypeNode.getJSType();
}
if (newType != null) {
var.setType(newType);
astParameter.setJSType(newType);
}
}
if (parameterTypeNode != null) {
parameterTypeNode = parameterTypeNode.getNext();
}
if (iifeArgumentNode != null) {
iifeArgumentNode = iifeArgumentNode.getNext();
}
}
}
}
}
@Override
FlowScope createInitialEstimateLattice() {
return bottomScope;
}
@Override
FlowScope createEntryLattice() {
return functionScope;
}
@Override
FlowScope flowThrough(Node n, FlowScope input) {
// If we have not walked a path from <entry> to <n>, then we don't
// want to infer anything about this scope.
if (input == bottomScope) {
return input;
}
FlowScope output = input.createChildFlowScope();
output = traverse(n, output);
return output;
}
@Override
@SuppressWarnings({"fallthrough", "incomplete-switch"})
List<FlowScope> branchedFlowThrough(Node source, FlowScope input) {
// NOTE(nicksantos): Right now, we just treat ON_EX edges like UNCOND
// edges. If we wanted to be perfect, we'd actually JOIN all the out
// lattices of this flow with the in lattice, and then make that the out
// lattice for the ON_EX edge. But it's probably too expensive to be
// worthwhile.
FlowScope output = flowThrough(source, input);
Node condition = null;
FlowScope conditionFlowScope = null;
BooleanOutcomePair conditionOutcomes = null;
List<DiGraphEdge<Node, Branch>> branchEdges = getCfg().getOutEdges(source);
List<FlowScope> result = Lists.newArrayListWithCapacity(branchEdges.size());
for (DiGraphEdge<Node, Branch> branchEdge : branchEdges) {
Branch branch = branchEdge.getValue();
FlowScope newScope = output;
switch (branch) {
case ON_TRUE:
if (NodeUtil.isForIn(source)) {
// item is assigned a property name, so its type should be string.
Node item = source.getFirstChild();
Node obj = item.getNext();
FlowScope informed = traverse(obj, output.createChildFlowScope());
if (item.isVar()) {
item = item.getFirstChild();
}
if (item.isName()) {
JSType iterKeyType = getNativeType(STRING_TYPE);
ObjectType objType = getJSType(obj).dereference();
JSType objIndexType = objType == null ?
null : objType.getTemplateTypeMap().
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Type, Node call) {
if (fnType.getTemplateTypeMap().getTemplateKeys().isEmpty()) {
return Collections.emptyMap();
}
Map<TemplateType, JSType> resolvedTypes = Maps.newIdentityHashMap();
Node callTarget = call.getFirstChild();
if (NodeUtil.isGet(callTarget)) {
Node obj = callTarget.getFirstChild();
maybeResolveTemplatedType(
fnType.getTypeOfThis(),
getJSType(obj),
resolvedTypes);
}
if (call.hasMoreThanOneChild()) {
maybeResolveTemplateTypeFromNodes(
fnType.getParameters(),
call.getChildAtIndex(1).siblings(),
resolvedTypes);
}
return resolvedTypes;
}
private void maybeResolveTemplatedType(
JSType paramType,
JSType argType,
Map<TemplateType, JSType> resolvedTypes) {
if (paramType.isTemplateType()) {
// @param {T}
resolvedTemplateType(
resolvedTypes, paramType.toMaybeTemplateType(), argType);
} else if (paramType.isUnionType()) {
// @param {Array.<T>|NodeList|Arguments|{length:number}}
UnionType unionType = paramType.toMaybeUnionType();
for (JSType alernative : unionType.getAlternates()) {
maybeResolveTemplatedType(alernative, argType, resolvedTypes);
}
} else if (paramType.isFunctionType()) {
FunctionType paramFunctionType = paramType.toMaybeFunctionType();
FunctionType argFunctionType = argType
.restrictByNotNullOrUndefined()
.collapseUnion()
.toMaybeFunctionType();
if (argFunctionType != null && argFunctionType.isSubtype(paramType)) {
// infer from return type of the function type
maybeResolveTemplatedType(
paramFunctionType.getTypeOfThis(),
argFunctionType.getTypeOfThis(), resolvedTypes);
// infer from return type of the function type
maybeResolveTemplatedType(
paramFunctionType.getReturnType(),
argFunctionType.getReturnType(), resolvedTypes);
// infer from parameter types of the function type
maybeResolveTemplateTypeFromNodes(
paramFunctionType.getParameters(),
argFunctionType.getParameters(), resolvedTypes);
}
} else if (paramType.isTemplatizedType()) {
// @param {Array.<T>}
ObjectType referencedParamType = paramType
.toMaybeTemplatizedType()
.getReferencedType();
JSType argObjectType = argType
.restrictByNotNullOrUndefined()
.collapseUnion();
if (argObjectType.isSubtype(referencedParamType)) {
// If the argument type is a subtype of the parameter type, resolve any
// template types amongst their templatized types.
TemplateTypeMap paramTypeMap = paramType.getTemplateTypeMap();
TemplateTypeMap argTypeMap = argObjectType.getTemplateTypeMap();
for (TemplateType key : paramTypeMap.getTemplateKeys()) {
maybeResolveTemplatedType(
paramTypeMap.getTemplateType(key),
argTypeMap.getTemplateType(key),
resolvedTypes);
}
}
}
}
private void maybeResolveTemplateTypeFromNodes(
Iterable<Node> declParams,
Iterable<Node> callParams,
Map<TemplateType, JSType> resolvedTypes) {
maybeResolveTemplateTypeFromNodes(
declParams.iterator(), callParams.iterator(),
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> resolvedTypes);
}
private void maybeResolveTemplateTypeFromNodes(
Iterator<Node> declParams,
Iterator<Node> callParams,
Map<TemplateType, JSType> resolvedTypes) {
while (declParams.hasNext() && callParams.hasNext()) {
Node declParam = declParams.next();
maybeResolveTemplatedType(
getJSType(declParam),
getJSType(callParams.next()),
resolvedTypes);
if (declParam.isVarArgs()) {
while (callParams.hasNext()) {
maybeResolveTemplatedType(
getJSType(declParam),
getJSType(callParams.next()),
resolvedTypes);
}
}
}
}
private static void resolvedTemplateType(
Map<TemplateType, JSType> map, TemplateType template, JSType resolved) {
JSType previous = map.get(template);
if (!resolved.isUnknownType()) {
if (previous == null) {
map.put(template, resolved);
} else {
JSType join = previous.getLeastSupertype(resolved);
map.put(template, join);
}
}
}
private static class TemplateTypeReplacer extends ModificationVisitor {
private final Map<TemplateType, JSType> replacements;
private final JSTypeRegistry registry;
boolean madeChanges = false;
TemplateTypeReplacer(
JSTypeRegistry registry, Map<TemplateType, JSType> replacements) {
super(registry);
this.registry = registry;
this.replacements = replacements;
}
@Override
public JSType caseTemplateType(TemplateType type) {
madeChanges = true;
private FlowScope traverseNew(Node n, FlowScope scope) {
scope = traverseChildren(n, scope);
Node constructor = n.getFirstChild();
JSType constructorType = constructor.getJSType();
JSType type = null;
if (constructorType != null) {
constructorType = constructorType.restrictByNotNullOrUndefined();
if (constructorType.isUnknownType()) {
type = unknownType;
} else {
FunctionType ct = constructorType.toMaybeFunctionType();
if (ct == null && constructorType instanceof FunctionType) {
// If constructorType is a NoObjectType, then toMaybeFunctionType will
// return null. But NoObjectType implements the FunctionType
// interface, precisely because it can validly construct objects.
ct = (FunctionType) constructorType;
}
if (ct != null && ct.isConstructor()) {
backwardsInferenceFromCallSite(n, ct);
// If necessary, create a TemplatizedType wrapper around the instance
// type, based on the types of the constructor parameters.
ObjectType instanceType = ct.getInstanceType();
Map<TemplateType, JSType> inferredTypes =
inferTemplateTypesFromParameters(ct, n);
if (inferredTypes.isEmpty()) {
type = instanceType;
} else {
type = registry.createTemplatizedType(instanceType, inferredTypes);
}
}
}
}
n.setJSType(type);
return scope;
}
private BooleanOutcomePair traverseAnd(Node n, FlowScope scope) {
return traverseShortCircuitingBinOp(n, scope, true);
}
private FlowScope traverseChildren(Node n, FlowScope scope
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2004 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.*;
/**
* Verifies that constants are only assigned a value once.
* e.g. var XX = 5;
* XX = 3; // error!
* XX++; // error!
*
*/
class ConstCheck extends AbstractPostOrderCallback
implements CompilerPass {
static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR =
DiagnosticType.error(
"JSC_CONSTANT_REASSIGNED_VALUE_ERROR",
"constant {0} assigned a value more than once");
private final AbstractCompiler compiler;
private final Set<Scope.Var> initializedConstants;
/**
* Creates an instance.
*/
public ConstCheck(AbstractCompiler compiler) {
this.compiler = compiler;
this.initializedConstants = new HashSet<Scope.Var>();
}
@Override
public void process(Node externs, Node root) {
Preconditions.checkState(compiler.getLifeCycleStage().isNormalized());
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
if (parent != null &&
parent.isVar() &&
n.hasChildren()) {
String name = n.getString();
Scope.Var var = t.getScope().getVar(name);
if (isConstant(var)) {
if (initializedConstants.contains(var)) {
reportError(t, n, name);
} else {
initializedConstants.add(var);
}
}
}
break;
case Token.ASSIGN:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD: {
Node lhs = n.getFirstChild();
if (lhs.isName()) {
String name = lhs.getString();
Scope.Var var = t.getScope().getVar(name);
if (isConstant(var)) {
if (initializedConstants.contains(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Map UNIQUE = new UniqueRenamingToken();
private static class UniqueRenamingToken implements RenamingMap {
@Override public String get(String value) { return null; }
}
public ReplaceIdGenerators(
AbstractCompiler compiler, Map<String, RenamingMap> idGens,
boolean generatePseudoNames,
String previousMapSerialized) {
this.compiler = compiler;
this.generatePseudoNames = generatePseudoNames;
nameGenerators = Maps.newLinkedHashMap();
idGeneratorMaps = Maps.newLinkedHashMap();
consistNameMap = Maps.newLinkedHashMap();
Map<String, BiMap<String, String>> previousMap;
previousMap = parsePreviousResults(previousMapSerialized);
this.previousMap = previousMap;
if (idGens != null) {
for (Entry<String, RenamingMap> gen : idGens.entrySet()) {
String name = gen.getKey();
RenamingMap map = gen.getValue();
if (map instanceof UniqueRenamingToken) {
nameGenerators.put(name,
createNameSupplier(
RenameStrategy.INCONSISTENT, previousMap.get(name)));
} else {
nameGenerators.put(name,
createNameSupplier(
RenameStrategy.MAPPED, map));
}
idGeneratorMaps.put(name, Maps.<String, String>newLinkedHashMap());
}
}
}
enum RenameStrategy {
CONSISTENT,
INCONSISTENT,
MAPPED,
STABLE
}
private static interface NameSupplier {
String getName(String id, String name);
RenameStrategy getRenameStrategy();
}
private static class ObfuscatedNameSuppier implements NameSupplier {
private final NameGenerator generator;
private final Map<String, String> previousMappings;
private RenameStrategy renameStrategy;
public ObfuscatedNameSuppier(
RenameStrategy renameStrategy, BiMap<String, String> previousMappings) {
this.previousMappings = previousMappings.inverse();
this.generator =
new NameGenerator(previousMappings.keySet(), "", null);
this.renameStrategy = renameStrategy;
}
@Override
public String getName(String id, String name) {
String newName = previousMappings.get(id);
if (newName == null) {
newName = generator.generateNextName();
}
return newName;
}
@Override
public RenameStrategy getRenameStrategy() {
return renameStrategy;
}
}
private static class PseudoNameSuppier implements NameSupplier {
private int counter = 0;
private RenameStrategy renameStrategy;
public PseudoNameSuppier(RenameStrategy renameStrategy) {
this.renameStrategy = renameStrategy;
}
@Override
public String getName(String id, String name) {
if (renameStrategy == RenameStrategy.INCONSISTENT) {
return name + "$" + counter++;
}
return name + "$0";
}
@Override
public RenameStrategy getRenameStrategy() {
return renameStrategy;
}
}
private static class StableNameSupplier implements NameSupplier {
@Override
public String getName(String id, String name) {
return Base64.base64EncodeInt(name.hashCode());
}
@Override
public RenameStrategy getRenameStrategy() {
return RenameStrategy.STABLE;
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> }
}
private static class MappedNameSupplier implements NameSupplier {
private final RenamingMap map;
MappedNameSupplier(RenamingMap map) {
this.map = map;
}
@Override
public String getName(String id, String name) {
return map.get(name);
}
@Override
public RenameStrategy getRenameStrategy() {
return RenameStrategy.MAPPED;
}
}
private NameSupplier createNameSupplier(
RenameStrategy renameStrategy, BiMap<String, String> previousMappings) {
previousMappings = previousMappings != null ?
previousMappings :
ImmutableBiMap.<String, String>of();
if (renameStrategy == RenameStrategy.STABLE) {
return new StableNameSupplier();
} else if (generatePseudoNames) {
return new PseudoNameSuppier(renameStrategy);
} else {
return new ObfuscatedNameSuppier(renameStrategy, previousMappings);
}
}
private NameSupplier createNameSupplier(
RenameStrategy renameStrategy, RenamingMap mappings) {
Preconditions.checkState(renameStrategy == RenameStrategy.MAPPED);
return new MappedNameSupplier(mappings);
}
private class GatherGenerators extends AbstractPostOrderCallback {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
JSDocInfo doc = n.getJSDocInfo();
if (doc == null) {
return;
}
int numGeneratorAnnotations =
(doc.isConsistentIdGenerator() ? 1 : 0) +
(doc.isIdGenerator() ? 1 : 0) +
(doc.isStableIdGenerator() ? 1 : 0) +
(doc.isMappedIdGenerator() ? 1 : 0);
if (numGeneratorAnnotations == 0) {
return;
} else if (numGeneratorAnnotations > 1) {
compiler.report(t.makeError(n, CONFLICTING_GENERATOR_TYPE));
}
String name = null;
if (n.isAssign()) {
name = n.getFirstChild().getQualifiedName();
} else if (n.isVar()) {
name = n.getFirstChild().getString();
} else if (n.isFunction()){
name = n.getFirstChild().getString();
if (name.isEmpty()) {
return;
}
}
if (doc.isConsistentIdGenerator()) {
consistNameMap.put(name, Maps.<String, String>newLinkedHashMap());
nameGenerators.put(
name, createNameSupplier(
RenameStrategy.CONSISTENT, previousMap.get(name)));
} else if (doc.isStableIdGenerator()) {
nameGenerators.put(
name, createNameSupplier(
RenameStrategy.STABLE, previousMap.get(name)));
} else if (doc.isIdGenerator()) {
nameGenerators.put(
name, createNameSupplier(
RenameStrategy.INCONSISTENT, previousMap.get(name)));
} else if (doc.isMappedIdGenerator()) {
NameSupplier supplier = nameGenerators.get(name);
if (supplier == null
|| supplier.getRenameStrategy() != RenameStrategy.MAPPED) {
compiler.report(t.makeError(n, MISSING_NAME_MAP
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>_FOR_GENERATOR));
// skip registering the name in the list of Generators if there no
// mapping.
return;
}
} else {
throw new IllegalStateException("unexpected");
}
idGeneratorMaps.put(name, Maps.<String, String>newLinkedHashMap());
}
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, new GatherGenerators());
if (!nameGenerators.isEmpty()) {
NodeTraversal.traverse(compiler, root, new ReplaceGenerators());
}
}
private class ReplaceGenerators extends AbstractPostOrderCallback {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (!n.isCall()) {
return;
}
String callName = n.getFirstChild().getQualifiedName();
NameSupplier nameGenerator = nameGenerators.get(callName);
if (nameGenerator == null) {
return;
}
if (!t.inGlobalScope() &&
nameGenerator.getRenameStrategy() == RenameStrategy.INCONSISTENT) {
// Warn about calls not in the global scope.
compiler.report(t.makeError(n, NON_GLOBAL_ID_GENERATOR_CALL));
return;
}
if (nameGenerator.getRenameStrategy() == RenameStrategy.INCONSISTENT) {
for (Node ancestor : n.getAncestors()) {
if (NodeUtil.isControlStructure(ancestor)) {
// Warn about conditional calls.
compiler.report(t.makeError(n, CONDITIONAL_ID_GENERATOR_CALL));
return;
}
}
}
Node arg = n.getFirstChild().getNext();
if (arg.isString()) {
String rename = getObfuscatedName(
arg, callName, nameGenerator, arg.getString());
parent.replaceChild(n, IR.string(rename));
compiler.reportCodeChange();
} else if (arg.isObjectLit()) {
for (Node key : arg.children()) {
String rename = getObfuscatedName(
key, callName, nameGenerator, key.getString());
key.setString(rename);
// Prevent standard renaming by marking the key as quoted.
key.putBooleanProp(Node.QUOTED_PROP, true);
}
arg.detachFromParent();
parent.replaceChild(n, arg);
compiler.reportCodeChange();
}
// TODO(user): Error on id not a string or object literal.
}
private String getObfuscatedName(
Node id, String callName, NameSupplier nameGenerator, String name) {
String rename = null;
Map<String, String> idGeneratorMap = idGeneratorMaps.get(callName);
String instanceId = getIdForGeneratorNode(
nameGenerator.getRenameStrategy() != RenameStrategy.INCONSISTENT, id);
if (nameGenerator.getRenameStrategy() == RenameStrategy.CONSISTENT) {
Map<String, String> entry = consistNameMap.get(callName);
rename = entry.get(instanceId);
if (rename == null) {
rename = nameGenerator.getName(instanceId, name);
entry.put(instanceId, rename);
}
} else {
rename = nameGenerator.getName(instanceId, name);
}
idGeneratorMap.put(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>rename, instanceId);
return rename;
}
}
/**
* @return The serialize map of generators and their ids and their
* replacements.
*/
public String getSerializedIdMappings() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Map<String, String>> replacements :
idGeneratorMaps.entrySet()) {
if (!replacements.getValue().isEmpty()) {
sb.append("[");
sb.append(replacements.getKey());
sb.append("]\n\n");
for (Map.Entry<String, String> replacement :
replacements.getValue().entrySet()) {
sb.append(replacement.getKey());
sb.append(':');
sb.append(replacement.getValue());
sb.append("\n");
}
sb.append("\n");
}
}
return sb.toString();
}
private Map<String, BiMap<String, String>> parsePreviousResults(
String serializedMap) {
//
// The expected format looks like this:
//
// [generatorName]
// someId:someFile:theLine:theColumn
//
//
if (serializedMap == null || serializedMap.isEmpty()) {
return Collections.emptyMap();
}
Map<String, BiMap<String, String>> resultMap = Maps.newHashMap();
BufferedReader reader = new BufferedReader(new StringReader(serializedMap));
BiMap<String, String> currentSectionMap = null;
String line;
int lineIndex = 0;
try {
while ((line = reader.readLine()) != null) {
lineIndex++;
if (line.isEmpty()) {
continue;
}
if (line.charAt(0) == '[') {
String currentSection = line.substring(1, line.length() - 1);
currentSectionMap = resultMap.get(currentSection);
if (currentSectionMap == null) {
currentSectionMap = HashBiMap.create();
resultMap.put(currentSection, currentSectionMap);
} else {
reportInvalidLine(line, lineIndex);
return Collections.emptyMap();
}
} else {
int split = line.indexOf(':');
if (split != -1) {
String name = line.substring(0, split);
String location = line.substring(split + 1, line.length());
currentSectionMap.put(name, location);
} else {
reportInvalidLine(line, lineIndex);
return Collections.emptyMap();
}
}
}
} catch (IOException e) {
JSError.make(INVALID_GENERATOR_ID_MAPPING, e.getMessage());
}
return resultMap;
}
private void reportInvalidLine(String line, int lineIndex) {
JSError.make(INVALID_GENERATOR_ID_MAPPING,
"line(" + line + "): " + lineIndex);
}
String getIdForGeneratorNode(boolean consistent, Node n) {
Preconditions.checkState(n.isString() || n.isStringKey());
if (consistent) {
return n.getString();
} else {
return n.getSourceFileName() + ':' + n.getLineno() + ":" + n.getCharno();
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.javascript.jscomp.NodeTraversal.Callback;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.JSTypeExpression;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.JSType;
import java.util.List;
import java.util.Set;
/**
* This pass walks the AST to create a Collection of 'new' nodes and
* 'goog.require' nodes. It reconciles these Collections, creating a
* warning for each discrepancy.
*
*/
class CheckRequiresForConstructors implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
private final CodingConvention codingConvention;
private final CheckLevel level;
// Warnings
static final DiagnosticType MISSING_REQUIRE_WARNING = DiagnosticType.disabled(
"JSC_MISSING_REQUIRE_WARNING",
"''{0}'' used but not goog.require''d");
CheckRequiresForConstructors(AbstractCompiler compiler,
CheckLevel level) {
this.compiler = compiler;
this.codingConvention = compiler.getCodingConvention();
this.level = level;
}
/**
* Uses Collections of new and goog.require nodes to create a compiler warning
* for each new class name without a corresponding goog.require().
*/
@Override
public void process(Node externs, Node root) {
Callback callback = new CheckRequiresForConstructorsCallback();
new NodeTraversal(compiler, callback).traverseRoots(externs, root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
Callback callback = new CheckRequiresForConstructorsCallback();
new NodeTraversal(compiler, callback).traverseWithScope(scriptRoot,
SyntacticScopeCreator.generateUntypedTopScope(compiler));
}
// Return true if the name is a class name (starts with an uppercase
// character, but is not in all caps).
private static boolean isClassName(String name) {
return (name != null && name.length() > 1
&& Character.isUpperCase(name.charAt(0))
&& !name.equals(name.toUpperCase()));
}
// Return the shortest prefix of the className that refers to a class,
// or null if no part refers to a class.
private static String getOutermostClassName
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>(String className) {
for (String part : className.split("\\.")) {
if (isClassName(part)) {
return className.substring(0, className.indexOf(part) +
part.length());
}
}
return null;
}
/**
* This class "records" each constructor and goog.require visited and creates
* a warning for each new node without an appropriate goog.require node.
*
*/
private class CheckRequiresForConstructorsCallback implements Callback {
private final List<String> constructors = Lists.newArrayList();
private final List<String> requires = Lists.newArrayList();
private final List<Node> newNodes = Lists.newArrayList();
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return parent == null || !parent.isScript() ||
!t.getInput().isExtern();
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.ASSIGN:
case Token.VAR:
maybeAddConstructor(t, n);
break;
case Token.FUNCTION:
// Exclude function expressions.
if (NodeUtil.isStatement(n)) {
maybeAddConstructor(t, n);
}
break;
case Token.CALL:
visitCallNode(n, parent);
break;
case Token.SCRIPT:
visitScriptNode(t);
break;
case Token.NEW:
visitNewNode(t, n);
}
}
private void visitScriptNode(NodeTraversal t) {
Set<String> classNames = Sets.newHashSet();
for (Node node : newNodes) {
String className = node.getFirstChild().getQualifiedName();
String outermostClassName = getOutermostClassName(className);
boolean notProvidedByConstructors =
(constructors == null || !constructors.contains(className));
boolean notProvidedByRequires =
(requires == null || (!requires.contains(className)
&& !requires.contains(outermostClassName)));
if (notProvidedByConstructors && notProvidedByRequires
&& !classNames.contains(className)) {
compiler.report(
t.makeError(node, level, MISSING_REQUIRE_WARNING, className));
classNames.add(className);
}
}
// for the next script, if there is one, we don't want the new, ctor, and
// require nodes to spill over.
this.newNodes.clear();
this.requires.clear();
this.constructors.clear();
}
private void visitCallNode(Node n, Node parent) {
String required = codingConvention.extractClassNameIfRequire(n, parent);
if (required != null) {
requires.add(required);
}
}
private void visitNewNode(NodeTraversal t, Node n) {
Node qNameNode = n.getFirstChild();
// If the ctor is something other than a qualified name, ignore it.
if (!qNameNode.isQualifiedName()) {
return;
}
// Grab the root ctor namespace.
Node nameNode = qNameNode;
for (; nameNode.hasChildren(); nameNode = nameNode.getFirstChild()) {}
// We only consider programmer-defined constructors that are
// global variables, or are defined on
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2012 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.javascript.jscomp.DefaultPassConfig.HotSwapPassFactory;
import com.google.javascript.jscomp.GlobalVarReferenceMap.GlobalVarRefCleanupPass;
import com.google.javascript.jscomp.Scope.Var;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import java.util.List;
/**
* Provides passes that should be run before hot-swap/incremental builds.
*
* @author tylerg@google.com (Tyler Goodwin)
*/
class CleanupPasses extends PassConfig {
private State state;
public CleanupPasses(CompilerOptions options) {
super(options);
}
@Override
protected List<PassFactory> getChecks() {
List<PassFactory> checks = Lists.newArrayList();
checks.add(fieldCleanupPassFactory);
checks.add(scopeCleanupPassFactory);
checks.add(globalVarRefCleanupPassFactory);
return checks;
}
@Override
protected State getIntermediateState() {
return state;
}
@Override
protected List<PassFactory> getOptimizations() {
return ImmutableList.of();
}
@Override
protected void setIntermediateState(State state) {
this.state = state;
}
final PassFactory fieldCleanupPassFactory =
new HotSwapPassFactory("FieldCleaupPassFactory", false) {
@Override
protected HotSwapCompilerPass create(
AbstractCompiler compiler) {
return new FieldCleanupPass(compiler);
}
};
final PassFactory scopeCleanupPassFactory =
new HotSwapPassFactory("ScopeCleanupPassFactory", false) {
@Override
protected HotSwapCompilerPass create(
AbstractCompiler compiler) {
return new MemoizedScopeCleanupPass(compiler);
}
};
final PassFactory globalVarRefCleanupPassFactory =
new HotSwapPassFactory("GlobalVarRefCleanupPassFactory", false) {
@Override
protected HotSwapCompilerPass create(
AbstractCompiler compiler) {
return new GlobalVarRefCleanupPass(compiler);
}
};
/**
* A CleanupPass implementation that will remove stored scopes from the
* MemoizedScopeCreator of the compiler instance for a the hot swapped script.
* <p>
* This pass will also clear out Source Nodes of Function Types declared on
* Vars tracked by Memoized
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>ScopeCreator
*/
static class MemoizedScopeCleanupPass implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
public MemoizedScopeCleanupPass(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
ScopeCreator creator = compiler.getTypedScopeCreator();
if (creator instanceof MemoizedScopeCreator) {
MemoizedScopeCreator scopeCreator = (MemoizedScopeCreator) creator;
String newSrc = scriptRoot.getSourceFileName();
for (Var var : scopeCreator.getAllSymbols()) {
JSType type = var.getType();
if (type != null) {
FunctionType fnType = type.toMaybeFunctionType();
if (fnType != null
&& newSrc.equals(NodeUtil.getSourceName(fnType.getSource()))) {
fnType.setSource(null);
}
}
}
scopeCreator.removeScopesForScript(originalRoot.getSourceFileName());
}
}
@Override
public void process(Node externs, Node root) {
// MemoizedScopeCleanupPass should not do work during process.
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2011 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.jstype.StaticScope;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Helper classes for dealing with coding conventions.
*/
public class CodingConventions {
private CodingConventions() {}
/** Gets the default coding convention. */
public static CodingConvention getDefault() {
return new DefaultCodingConvention();
}
/**
* A convention that wraps another.
*
* When you want to support a new library, you should subclass this
* delegate, and override the methods that you want to customize.
*
* This way, a person using jQuery and Closure Library can create a new
* coding convention by creating a jQueryCodingConvention that delegates
* to a ClosureCodingConvention that delegates to a DefaultCodingConvention.
*/
public static class Proxy implements CodingConvention {
protected final CodingConvention nextConvention;
protected Proxy(CodingConvention convention) {
this.nextConvention = convention;
}
@Override
public boolean isConstant(String variableName) {
return nextConvention.isConstant(variableName);
}
@Override public boolean isConstantKey(String keyName) {
return nextConvention.isConstantKey(keyName);
}
@Override
public boolean isValidEnumKey(String key) {
return nextConvention.isValidEnumKey(key);
}
@Override
public boolean isOptionalParameter(Node parameter) {
return nextConvention.isOptionalParameter(parameter);
}
@Override
public boolean isVarArgsParameter(Node parameter) {
return nextConvention.isVarArgsParameter(parameter);
}
@Override
public boolean isExported(String name, boolean local) {
return nextConvention.isExported(name, local);
}
@Override
public final boolean isExported(String name) {
return isExported(name, false) || isExported(name, true);
}
@Override
public boolean isPrivate(String name) {
return nextConvention.isPrivate(name);
}
@Override
public SubclassRelationship getClasses
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>DefinedByCall(Node callNode) {
return nextConvention.getClassesDefinedByCall(callNode);
}
@Override
public boolean isSuperClassReference(String propertyName) {
return nextConvention.isSuperClassReference(propertyName);
}
@Override
public String extractClassNameIfProvide(Node node, Node parent) {
return nextConvention.extractClassNameIfProvide(node, parent);
}
@Override
public String extractClassNameIfRequire(Node node, Node parent) {
return nextConvention.extractClassNameIfRequire(node, parent);
}
@Override
public String getExportPropertyFunction() {
return nextConvention.getExportPropertyFunction();
}
@Override
public String getExportSymbolFunction() {
return nextConvention.getExportSymbolFunction();
}
@Override
public List<String> identifyTypeDeclarationCall(Node n) {
return nextConvention.identifyTypeDeclarationCall(n);
}
@Override
public void applySubclassRelationship(FunctionType parentCtor,
FunctionType childCtor, SubclassType type) {
nextConvention.applySubclassRelationship(
parentCtor, childCtor, type);
}
@Override
public String getAbstractMethodName() {
return nextConvention.getAbstractMethodName();
}
@Override
public String getSingletonGetterClassName(Node callNode) {
return nextConvention.getSingletonGetterClassName(callNode);
}
@Override
public void applySingletonGetter(FunctionType functionType,
FunctionType getterType, ObjectType objectType) {
nextConvention.applySingletonGetter(
functionType, getterType, objectType);
}
@Override
public boolean isInlinableFunction(Node n) {
return nextConvention.isInlinableFunction(n);
}
@Override
public DelegateRelationship getDelegateRelationship(Node callNode) {
return nextConvention.getDelegateRelationship(callNode);
}
@Override
public void applyDelegateRelationship(
ObjectType delegateSuperclass, ObjectType delegateBase,
ObjectType delegator, FunctionType delegateProxy,
FunctionType findDelegate) {
nextConvention.applyDelegateRelationship(
delegateSuperclass, delegateBase, delegator,
delegateProxy, findDelegate);
}
@Override
public String getDelegateSuperclassName() {
return nextConvention.getDelegateSuperclassName();
}
@Override
public void checkForCallingConventionDefiningCalls(
Node n, Map<String, String> delegateCallingConventions) {
nextConvention.checkForCallingConventionDefiningCalls(
n, delegateCallingConventions);
}
@Override
public void defineDelegateProxyPrototypeProperties(
JSTypeRegistry registry, StaticScope<JSType> scope,
List<ObjectType> delegateProxyPrototypes,
Map<String, String> delegateCallingConventions) {
nextConvention.defineDelegateProxyPrototypeProperties(
registry, scope, delegateProxyPrototypes, delegateCallingConventions);
}
@Override
public String getGlobalObject() {
return nextConvention.getGlobalObject();
}
@Override
public Collection<AssertionFunctionSpec> getAssertionFunctions() {
return nextConvention.getAssertionFunctions();
}
@Override
public Bind describeFunctionBind(Node n) {
return describeFunctionBind(n, false);
}
@Override
public Bind describeFunctionBind(Node n, boolean useTypeInfo) {
return nextConvention.describeFunctionBind(n, useTypeInfo);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> }
@Override
public boolean isPropertyTestFunction(Node call) {
return nextConvention.isPropertyTestFunction(call);
}
@Override
public boolean isPrototypeAlias(Node getProp) {
return false;
}
@Override
public ObjectLiteralCast getObjectLiteralCast(Node callNode) {
return nextConvention.getObjectLiteralCast(callNode);
}
@Override
public Collection<String> getIndirectlyDeclaredProperties() {
return nextConvention.getIndirectlyDeclaredProperties();
}
}
/**
* The default coding convention.
* Should be at the bottom of all proxy chains.
*/
private static class DefaultCodingConvention implements CodingConvention {
private static final long serialVersionUID = 1L;
@Override
public boolean isConstant(String variableName) {
return false;
}
@Override
public boolean isConstantKey(String variableName) {
return false;
}
@Override
public boolean isValidEnumKey(String key) {
return key != null && key.length() > 0;
}
@Override
public boolean isOptionalParameter(Node parameter) {
// be as lax as possible, but this must be mutually exclusive from
// var_args parameters.
return false;
}
@Override
public boolean isVarArgsParameter(Node parameter) {
// be as lax as possible
return false;
}
@Override
public boolean isExported(String name, boolean local) {
return local && name.startsWith("$super");
}
@Override
public boolean isExported(String name) {
return isExported(name, false) || isExported(name, true);
}
@Override
public boolean isPrivate(String name) {
return false;
}
@Override
public SubclassRelationship getClassesDefinedByCall(Node callNode) {
return null;
}
@Override
public boolean isSuperClassReference(String propertyName) {
return false;
}
@Override
public String extractClassNameIfProvide(Node node, Node parent) {
String message = "only implemented in GoogleCodingConvention";
throw new UnsupportedOperationException(message);
}
@Override
public String extractClassNameIfRequire(Node node, Node parent) {
String message = "only implemented in GoogleCodingConvention";
throw new UnsupportedOperationException(message);
}
@Override
public String getExportPropertyFunction() {
return null;
}
@Override
public String getExportSymbolFunction() {
return null;
}
@Override
public List<String> identifyTypeDeclarationCall(Node n) {
return null;
}
@Override
public void applySubclassRelationship(FunctionType parentCtor,
FunctionType childCtor, SubclassType type) {
// do nothing
}
@Override
public String getAbstractMethodName() {
return null;
}
@Override
public String getSingletonGetterClassName(Node callNode) {
return null;
}
@Override
public void applySingletonGetter(FunctionType functionType,
FunctionType getterType, ObjectType objectType) {
// do nothing.
}
@Override
public boolean isInlinableFunction(Node n) {
Preconditions.checkState(n.isFunction());
return true;
}
@Override
public DelegateRelationship getDelegateRelationship(Node callNode) {
return null;
}
@Override
public void apply
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>DelegateRelationship(
ObjectType delegateSuperclass, ObjectType delegateBase,
ObjectType delegator, FunctionType delegateProxy,
FunctionType findDelegate) {
// do nothing.
}
@Override
public String getDelegateSuperclassName() {
return null;
}
@Override
public void checkForCallingConventionDefiningCalls(Node n,
Map<String, String> delegateCallingConventions) {
// do nothing.
}
@Override
public void defineDelegateProxyPrototypeProperties(
JSTypeRegistry registry, StaticScope<JSType> scope,
List<ObjectType> delegateProxyPrototypes,
Map<String, String> delegateCallingConventions) {
// do nothing.
}
@Override
public String getGlobalObject() {
return "window";
}
@Override
public boolean isPropertyTestFunction(Node call) {
return false;
}
@Override
public boolean isPrototypeAlias(Node getProp) {
return false;
}
@Override
public ObjectLiteralCast getObjectLiteralCast(Node callNode) {
return null;
}
@Override
public Collection<AssertionFunctionSpec> getAssertionFunctions() {
return Collections.emptySet();
}
@Override
public Bind describeFunctionBind(Node n) {
return describeFunctionBind(n, false);
}
@Override
public Bind describeFunctionBind(Node n, boolean useTypeInfo) {
if (!n.isCall()) {
return null;
}
Node callTarget = n.getFirstChild();
String name = callTarget.getQualifiedName();
if (name != null) {
if (name.equals("Function.prototype.bind.call")) {
// goog.bind(fn, self, args...);
Node fn = callTarget.getNext();
if (fn == null) {
return null;
}
Node thisValue = safeNext(fn);
Node parameters = safeNext(thisValue);
return new Bind(fn, thisValue, parameters);
}
}
if (callTarget.isGetProp()
&& callTarget.getLastChild().getString().equals("bind")) {
Node maybeFn = callTarget.getFirstChild();
JSType maybeFnType = maybeFn.getJSType();
FunctionType fnType = null;
if (useTypeInfo && maybeFnType != null) {
fnType = maybeFnType.restrictByNotNullOrUndefined()
.toMaybeFunctionType();
}
if (fnType != null || maybeFn.isFunction()) {
// (function(){}).bind(self, args...);
Node thisValue = callTarget.getNext();
Node parameters = safeNext(thisValue);
return new Bind(maybeFn, thisValue, parameters);
}
}
return null;
}
@Override
public Collection<String> getIndirectlyDeclaredProperties() {
return ImmutableList.of();
}
private Node safeNext(Node n) {
if (n != null) {
return n.getNext();
}
return null;
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import com.google.javascript.rhino.jstype.StaticSourceFile;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
* An abstract representation of a source file that provides access to
* language-neutral features. The source file can be loaded from various
* locations, such as from disk or from a preloaded string.
*
* @author nicksantos@google.com (Nick Santos)
*/
public class SourceFile implements StaticSourceFile, Serializable {
private static final long serialVersionUID = 1L;
/** A JavaScript source code provider. The value should
* be cached so that the source text stays consistent throughout a single
* compile. */
public interface Generator {
public String getCode();
}
/**
* Number of lines in the region returned by {@link #getRegion(int)}.
* This length must be odd.
*/
private static final int SOURCE_EXCERPT_REGION_LENGTH = 5;
private final String fileName;
private boolean isExternFile = false;
// The fileName may not always identify the original file - for example,
// supersourced Java inputs, or Java inputs that come from Jar files. This
// is an optional field that the creator of an AST or SourceFile can set.
// It could be a path to the original file, or in case this SourceFile came
// from a Jar, it could be the path to the Jar.
private String originalPath = null;
// Source Line Information
private int[] lineOffsets = null;
private String code = null;
/**
* Construct a new abstract source file.
*
* @param fileName The file name of the source file. It does not necessarily
* need to correspond to a real path. But it should be unique. Will
* appear in warning messages emitted by the compiler.
*/
public SourceFile(String fileName) {
if (fileName == null || fileName.isEmpty()) {
throw new IllegalArgumentException("a source must have a name");
}
this.fileName = fileName;
}
@
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Override
public int getLineOffset(int lineno) {
findLineOffsets();
if (lineno < 1 || lineno > lineOffsets.length) {
throw new IllegalArgumentException(
"Expected line number between 1 and " + lineOffsets.length +
"\nActual: " + lineno);
}
return lineOffsets[lineno - 1];
}
/** @return The number of lines in this source file. */
int getNumLines() {
findLineOffsets();
return lineOffsets.length;
}
private void findLineOffsets() {
if (lineOffsets != null) {
return;
}
try {
String[] sourceLines = getCode().split("\n");
lineOffsets = new int[sourceLines.length];
for (int ii = 1; ii < sourceLines.length; ++ii) {
lineOffsets[ii] =
lineOffsets[ii - 1] + sourceLines[ii - 1].length() + 1;
}
} catch (IOException e) {
lineOffsets = new int[1];
lineOffsets[0] = 0;
}
}
//////////////////////////////////////////////////////////////////////////////
// Implementation
/**
* Gets all the code in this source file.
* @throws IOException
*/
public String getCode() throws IOException {
return code;
}
/**
* Gets a reader for the code in this source file.
*/
public Reader getCodeReader() throws IOException {
return new StringReader(getCode());
}
@VisibleForTesting
String getCodeNoCache() {
return code;
}
private void setCode(String sourceCode) {
code = sourceCode;
}
public String getOriginalPath() {
return originalPath != null ? originalPath : fileName;
}
public void setOriginalPath(String originalPath) {
this.originalPath = originalPath;
}
// For SourceFile types which cache source code that can be regenerated
// easily, flush the cache. We maintain the cache mostly to speed up
// generating source when displaying error messages, so dumping the file
// contents after the compile is a fine thing to do.
public void clearCachedSource() {
// By default, do nothing. Not all kinds of SourceFiles can regenerate
// code.
}
boolean hasSourceInMemory() {
return code != null;
}
/** Returns a unique name for the source file. */
@Override
public String getName() {
return fileName;
}
/** Returns whether this is an extern. */
@Override
public boolean isExtern() {
return isExternFile;
}
/** Sets that this is an extern. */
void setIsExtern(boolean newVal) {
isExternFile = newVal;
}
@Override
public int getLineOfOffset(int offset) {
findLineOffsets();
int search = Arrays.binarySearch(lineOffsets, offset);
if (search >= 0) {
return search + 1; // lines are 1-based.
} else {
int insertionPoint = -1 * (search + 1);
return Math.min(insertionPoint - 1, lineOffsets.length - 1) + 1;
}
}
@Override
public int getColumnOfOffset(int offset) {
int line = getLineOfOffset(offset);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> endLine, js.substring(pos, last));
} else {
return new SimpleRegion(startLine, endLine, js.substring(pos));
}
} else {
return new SimpleRegion(startLine, endLine, js.substring(pos, end));
}
}
@Override
public String toString() {
return fileName;
}
public static SourceFile fromFile(String fileName, Charset c) {
return builder().withCharset(c).buildFromFile(fileName);
}
public static SourceFile fromFile(String fileName) {
return builder().buildFromFile(fileName);
}
public static SourceFile fromFile(File file, Charset c) {
return builder().withCharset(c).buildFromFile(file);
}
public static SourceFile fromFile(File file) {
return builder().buildFromFile(file);
}
public static SourceFile fromCode(String fileName, String code) {
return builder().buildFromCode(fileName, code);
}
public static SourceFile fromCode(String fileName,
String originalPath, String code) {
return builder().withOriginalPath(originalPath)
.buildFromCode(fileName, code);
}
public static SourceFile fromInputStream(String fileName, InputStream s)
throws IOException {
return builder().buildFromInputStream(fileName, s);
}
public static SourceFile fromInputStream(String fileName,
String originalPath, InputStream s) throws IOException {
return builder().withOriginalPath(originalPath)
.buildFromInputStream(fileName, s);
}
public static SourceFile fromReader(String fileName, Reader r)
throws IOException {
return builder().buildFromReader(fileName, r);
}
public static SourceFile fromGenerator(String fileName,
Generator generator) {
return builder().buildFromGenerator(fileName, generator);
}
/** Create a new builder for source files. */
public static Builder builder() {
return new Builder();
}
/**
* A builder interface for source files.
*
* Allows users to customize the Charset, and the original path of
* the source file (if it differs from the path on disk).
*/
public static class Builder {
private Charset charset = Charsets.UTF_8;
private String originalPath = null;
public Builder() {}
/** Set the charset to use when reading from an input stream or file. */
public Builder withCharset(Charset charset) {
this.charset = charset;
return this;
}
/** Set the original path to use. */
public Builder withOriginalPath(String originalPath) {
this.originalPath = originalPath;
return this;
}
public SourceFile buildFromFile(String fileName) {
return buildFromFile(new File(fileName));
}
public SourceFile buildFromFile(File file) {
return new OnDisk(file, originalPath, charset);
}
public SourceFile buildFromCode(String fileName, String code) {
return new Preloaded(fileName, originalPath, code);
}
public SourceFile buildFromInputStream(String fileName, InputStream s)
throws IOException {
return buildFromCode(fileName,
CharStreams.toString(new InputStreamReader(s, charset)));
}
public SourceFile buildFromReader(String fileName, Reader r)
throws IOException {
return buildFromCode(fileName, CharStreams.toString(r));
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> }
public SourceFile buildFromGenerator(String fileName,
Generator generator) {
return new Generated(fileName, originalPath, generator);
}
}
//////////////////////////////////////////////////////////////////////////////
// Implementations
/**
* A source file where the code has been preloaded.
*/
static class Preloaded extends SourceFile {
private static final long serialVersionUID = 1L;
Preloaded(String fileName, String originalPath, String code) {
super(fileName);
super.setOriginalPath(originalPath);
super.setCode(code);
}
}
/**
* A source file where the code will be dynamically generated
* from the injected interface.
*/
static class Generated extends SourceFile {
private static final long serialVersionUID = 1L;
private final Generator generator;
// Not private, so that LazyInput can extend it.
Generated(String fileName, String originalPath, Generator generator) {
super(fileName);
super.setOriginalPath(originalPath);
this.generator = generator;
}
@Override
public synchronized String getCode() throws IOException {
String cachedCode = super.getCode();
if (cachedCode == null) {
cachedCode = generator.getCode();
super.setCode(cachedCode);
}
return cachedCode;
}
// Clear out the generated code when finished with a compile; we can
// regenerate it if we ever need it again.
@Override
public void clearCachedSource() {
super.setCode(null);
}
}
/**
* A source file where the code is only read into memory if absolutely
* necessary. We will try to delay loading the code into memory as long as
* possible.
*/
static class OnDisk extends SourceFile {
private static final long serialVersionUID = 1L;
private final File file;
// This is stored as a String, but passed in and out as a Charset so that
// we can serialize the class.
// Default input file format for JSCompiler has always been UTF_8.
private String inputCharset = Charsets.UTF_8.name();
OnDisk(File file, String originalPath, Charset c) {
super(file.getPath());
this.file = file;
super.setOriginalPath(originalPath);
if (c != null) {
this.setCharset(c);
}
}
@Override
public synchronized String getCode() throws IOException {
String cachedCode = super.getCode();
if (cachedCode == null) {
cachedCode = Files.toString(file, this.getCharset());
super.setCode(cachedCode);
}
return cachedCode;
}
/**
* Gets a reader for the code in this source file.
*/
@Override
public Reader getCodeReader() throws IOException {
if (hasSourceInMemory()) {
return super.getCodeReader();
} else {
// If we haven't pulled the code into memory yet, don't.
return new FileReader(file);
}
}
// Flush the cached code after the compile; we can read it off disk
// if we need it again.
@Override
public void clearCachedSource() {
super.setCode(null);
}
/**
* Store the Charset specification as the string version of the name,
* rather than the Charset itself. This allows us to serialize the
* SourceFile
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2006 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Lists;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.JSDocInfoBuilder;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.List;
/**
* Checks for non side effecting statements such as
* <pre>
* var s = "this string is "
* "continued on the next line but you forgot the +";
* x == foo(); // should that be '='?
* foo();; // probably just a stray-semicolon. Doesn't hurt to check though
* </p>
* and generates warnings.
*
*/
final class CheckSideEffects extends AbstractPostOrderCallback
implements HotSwapCompilerPass {
static final DiagnosticType USELESS_CODE_ERROR = DiagnosticType.warning(
"JSC_USELESS_CODE",
"Suspicious code. {0}");
static final String PROTECTOR_FN = "JSCOMPILER_PRESERVE";
private final CheckLevel level;
private final List<Node> problemNodes = Lists.newArrayList();
private final AbstractCompiler compiler;
private final boolean protectSideEffectFreeCode;
CheckSideEffects(AbstractCompiler compiler, CheckLevel level,
boolean protectSideEffectFreeCode) {
this.compiler = compiler;
this.level = level;
this.protectSideEffectFreeCode = protectSideEffectFreeCode;
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
// Code with hidden side-effect code is common, for example
// accessing "el.offsetWidth" forces a reflow in browsers, to allow this
// will still allowing local dead code removal in general,
// protect the "side-effect free" code in the source.
//
if (protectSideEffectFreeCode) {
protectSideEffects();
}
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
NodeTraversal.traverse(compiler, scriptRoot, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
// VOID nodes appear when there are extra semicolons at the BLOCK level.
// I've been unable to think of any cases where this indicates a bug,
// and apparently some people like keeping these semicolons around,
// so we
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> class StripProtection extends AbstractPostOrderCallback implements CompilerPass {
private final AbstractCompiler compiler;
StripProtection(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isCall()) {
Node target = n.getFirstChild();
// TODO(johnlenz): add this to the coding convention
// so we can remove goog.reflect.sinkValue as well.
if (target.isName() && target.getString().equals(PROTECTOR_FN)) {
Node expr = n.getLastChild();
n.detachChildren();
parent.replaceChild(n, expr);
}
}
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>(templateValues);
this.registry = registry;
this.templateKeys = templateKeys;
int nKeys = templateKeys.size();
this.templateValues = templateValues.size() > nKeys ?
templateValues.subList(0, nKeys) : templateValues;
// Iteratively resolve any JSType values that refer to the TemplateType keys
// of this TemplateTypeMap.
TemplateTypeMapReplacer replacer = new TemplateTypeMapReplacer(
registry, this);
ImmutableList.Builder<JSType> builder = ImmutableList.builder();
for (JSType templateValue : this.templateValues) {
builder.add(templateValue.visit(replacer));
}
this.resolvedTemplateValues = builder.build();
}
/**
* Returns true if the map is empty; false otherwise.
*/
public boolean isEmpty() {
return templateKeys.isEmpty();
}
/**
* Returns a list of all template keys.
*/
public ImmutableList<TemplateType> getTemplateKeys() {
return templateKeys;
}
/**
* Returns true if this map contains the specified template key, false
* otherwise.
*/
public boolean hasTemplateKey(TemplateType templateKey) {
// Note: match by identity, not equality
for (TemplateType entry : templateKeys) {
if (entry == templateKey) {
return true;
}
}
return false;
}
/**
* Returns the number of template keys in this map that do not have a
* corresponding JSType value.
*/
int numUnfilledTemplateKeys() {
return templateKeys.size() - templateValues.size();
}
/**
* Returns a list of template keys in this map that do not have corresponding
* JSType values.
*/
ImmutableList<TemplateType> getUnfilledTemplateKeys() {
return templateKeys.subList(templateValues.size(), templateKeys.size());
}
/**
* Returns true if there is a JSType value associated with the specified
* template key; false otherwise.
*/
public boolean hasTemplateType(TemplateType key) {
return getTemplateTypeIndex(key) != -1;
}
/**
* Returns the JSType value associated with the specified template key. If no
* JSType value is associated, returns UNKNOWN_TYPE.
*/
public JSType getTemplateType(TemplateType key) {
int index = getTemplateTypeIndex(key);
return (index == -1) ? registry.getNativeType(JSTypeNative.UNKNOWN_TYPE) :
templateValues.get(index);
}
public TemplateType getTemplateTypeKeyByName(String keyName) {
for (TemplateType key : templateKeys) {
if (key.getReferenceName().equals(keyName)) {
return key;
}
}
return null;
}
/**
* Returns the index of the JSType value associated with the specified
* template key. If no JSType value is associated, returns -1.
*/
private int getTemplateTypeIndex(TemplateType key) {
int maxIndex = Math.min(templateKeys.size(), templateValues.size());
for (int i = maxIndex - 1; i >= 0; i--) {
if (templateKeys.get(i) == key) {
return i;
}
}
return -1;
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
private JSType getResolvedTemplateType(TemplateType key) {
int index = getTemplateTypeIndex(key);
return (index == -1) ? registry.getNativeType(JSTypeNative.UNKNOWN_TYPE) :
resolvedTemplateValues.get(index);
}
/**
* An enum tracking the three different equivalence match states for a
* template key-value pair.
*/
private enum EquivalenceMatch {
NO_KEY_MATCH, VALUE_MISMATCH, VALUE_MATCH
}
/**
* Determines if this map and the specified map have equivalent template
* types.
*/
public boolean checkEquivalenceHelper(
TemplateTypeMap that, EquivalenceMethod eqMethod) {
ImmutableList<TemplateType> thisKeys = getTemplateKeys();
ImmutableList<TemplateType> thatKeys = that.getTemplateKeys();
EquivalenceMatch[] thatMatches = new EquivalenceMatch[thatKeys.size()];
Arrays.fill(thatMatches, EquivalenceMatch.NO_KEY_MATCH);
for (int i = 0; i < thisKeys.size(); i++) {
TemplateType thisKey = thisKeys.get(i);
JSType thisType = getResolvedTemplateType(thisKey);
EquivalenceMatch thisMatch = EquivalenceMatch.NO_KEY_MATCH;
for (int j = 0; j < thatKeys.size(); j++) {
TemplateType thatKey = thatKeys.get(j);
JSType thatType = that.getResolvedTemplateType(thatKey);
// Cross-compare every key-value pair in this TemplateTypeMap with
// those in that TemplateTypeMap. Update the Equivalence match for both
// key-value pairs involved.
if (thisKey == thatKey) {
EquivalenceMatch newMatchType = EquivalenceMatch.VALUE_MISMATCH;
if (thisType.checkEquivalenceHelper(thatType, eqMethod)) {
newMatchType = EquivalenceMatch.VALUE_MATCH;
}
if (thisMatch != EquivalenceMatch.VALUE_MATCH) {
thisMatch = newMatchType;
}
if (thatMatches[j] != EquivalenceMatch.VALUE_MATCH) {
thatMatches[j] = newMatchType;
}
}
}
if (failedEquivalenceCheck(thisMatch, eqMethod)) {
return false;
}
}
for (int i = 0; i < thatMatches.length; i++) {
if (failedEquivalenceCheck(thatMatches[i], eqMethod)) {
return false;
}
}
return true;
}
/**
* Determines if the specified EquivalenceMatch is considered a failing
* condition for an equivalence check, given the EquivalenceMethod used for
* the check.
*/
private boolean failedEquivalenceCheck(
EquivalenceMatch eqMatch, EquivalenceMethod eqMethod) {
return eqMatch == EquivalenceMatch.VALUE_MISMATCH ||
(eqMatch == EquivalenceMatch.NO_KEY_MATCH &&
eqMethod != EquivalenceMethod.INVARIANT);
}
/**
* Extends this TemplateTypeMap with the contents of the specified map.
* UNKNOWN_TYPE will be used as the value for any missing values in the
* specified map.
*/
TemplateTypeMap extend(TemplateTypeMap thatMap) {
thatMap = thatMap.addUnknownValues();
return registry.createTemplate
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>TypeMap(
concatImmutableLists(thatMap.templateKeys, templateKeys),
concatImmutableLists(thatMap.templateValues, templateValues));
}
/**
* Returns a new TemplateTypeMap whose values have been extended with the
* specified list.
*/
TemplateTypeMap addValues(ImmutableList<JSType> newValues) {
// Ignore any new template values that will not align with an existing
// template key.
int numUnfilledKeys = numUnfilledTemplateKeys();
if (numUnfilledKeys < newValues.size()) {
newValues = newValues.subList(0, numUnfilledKeys);
}
return registry.createTemplateTypeMap(
templateKeys, concatImmutableLists(templateValues, newValues));
}
/**
* Returns a new TemplateTypeMap, where all unfilled values have been filled
* with UNKNOWN_TYPE.
*/
private TemplateTypeMap addUnknownValues() {
int numUnfilledTemplateKeys = numUnfilledTemplateKeys();
if (numUnfilledTemplateKeys == 0) {
return this;
}
ImmutableList.Builder<JSType> builder = ImmutableList.builder();
for (int i = 0; i < numUnfilledTemplateKeys; i++) {
builder.add(registry.getNativeType(JSTypeNative.UNKNOWN_TYPE));
}
return addValues(builder.build());
}
/**
* Concatenates two ImmutableList instances. If either input is empty, the
* other is returned; otherwise, a new ImmutableList instance is created that
* contains the contents of both arguments.
*/
private <T> ImmutableList<T> concatImmutableLists(
ImmutableList<T> first, ImmutableList<T> second) {
if (first.isEmpty()) {
return second;
}
if (second.isEmpty()) {
return first;
}
ImmutableList.Builder<T> builder = ImmutableList.builder();
builder.addAll(first);
builder.addAll(second);
return builder.build();
}
boolean hasAnyTemplateTypesInternal() {
for (JSType templateValue : templateValues) {
if (templateValue.hasAnyTemplateTypes()) {
return true;
}
}
return false;
}
@Override
public String toString() {
String s = "";
int len = Math.max(Math.max(templateKeys.size(), templateValues.size()),
resolvedTemplateValues.size());
s += "{ ";
for (int i = 0; i < len; i++) {
s += "(";
s += (i < templateKeys.size()) ? templateKeys.get(i) : "";
s += ",";
s += (i < templateValues.size()) ? templateValues.get(i) : "";
s += ",";
s += (i < resolvedTemplateValues.size()) ? resolvedTemplateValues.get(i) : "";
s += ") ";
}
s += "}";
return s;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.Node;
import java.util.regex.Pattern;
/**
* This describes the Google-specific JavaScript coding conventions.
* Within Google, variable names are semantically significant.
*
*/
public class GoogleCodingConvention extends CodingConventions.Proxy {
private static final long serialVersionUID = 1L;
private static final String OPTIONAL_ARG_PREFIX = "opt_";
private static final String VAR_ARGS_NAME = "var_args";
private static final Pattern ENUM_KEY_PATTERN =
Pattern.compile("[A-Z0-9][A-Z0-9_]*");
/** By default, decorate the ClosureCodingConvention. */
public GoogleCodingConvention() {
this(new ClosureCodingConvention());
}
/** Decorates a wrapped CodingConvention. */
public GoogleCodingConvention(CodingConvention convention) {
super(convention);
}
/**
* {@inheritDoc}
*
* <p>This enforces the Google const name convention, that the first character
* after the last $ must be an upper-case letter and all subsequent letters
* must be upper case. The name must be at least 2 characters long.
*
* <p>Examples:
* <pre>
* aaa Not constant - lower-case letters in the name
* A Not constant - too short
* goog$A Constant - letters after the $ are upper-case.
* AA17 Constant - digits can appear after the first letter
* goog$7A Not constant - first character after the $ must be
* upper case.
* $A Constant - doesn't have to be anything in front of the $
* </pre>
*/
@Override
public boolean isConstant(String name) {
if (name.length() <= 1) {
return false;
}
// In compiled code, '$' is often a namespace delimiter. To allow inlining
// of namespaced constants, we strip off any namespaces here.
int pos = name.lastIndexOf('$');
if (pos >= 0) {
name = name.substring(pos + 1);
if (name.length() == 0) {
return false;
}
}
return isConstantKey(name);
}
@Override
public boolean isConstantKey(String name) {
if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) {
return false;
}
// hack way of checking that there aren't any lower-case letters
return name.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>toUpperCase().equals(name);
}
/**
* {@inheritDoc}
*
* <p>This enforces Google's convention about enum key names. They must match
* the regular expression {@code [A-Z0-9][A-Z0-9_]*}.
*
* <p>Examples:
* <ul>
* <li>A</li>
* <li>213</li>
* <li>FOO_BAR</li>
* </ul>
*/
@Override
public boolean isValidEnumKey(String key) {
return ENUM_KEY_PATTERN.matcher(key).matches();
}
/**
* {@inheritDoc}
*
* <p>In Google code, parameter names beginning with {@code opt_} are
* treated as optional arguments.
*/
@Override
public boolean isOptionalParameter(Node parameter) {
return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX);
}
@Override
public boolean isVarArgsParameter(Node parameter) {
return VAR_ARGS_NAME.equals(parameter.getString());
}
/**
* {@inheritDoc}
*
* <p>In Google code, any global name starting with an underscore is
* considered exported.
*/
@Override
public boolean isExported(String name, boolean local) {
return super.isExported(name, local) ||
(!local && name.startsWith("_"));
}
/**
* {@inheritDoc}
*
* <p>In Google code, private names end with an underscore, and exported
* names are never considered private (see {@link #isExported}).
*/
@Override
public boolean isPrivate(String name) {
return name.endsWith("_") && !isExported(name);
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Predicate;
import com.google.javascript.jscomp.ControlFlowGraph.Branch;
import com.google.javascript.jscomp.NodeTraversal.ScopedCallback;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.TernaryValue;
/**
* Checks functions for missing return statements. Return statements are only
* expected for functions with return type information. Functions with empty
* bodies are ignored.
*
*/
class CheckMissingReturn implements ScopedCallback {
static final DiagnosticType MISSING_RETURN_STATEMENT =
DiagnosticType.warning(
"JSC_MISSING_RETURN_STATEMENT",
"Missing return statement. Function expected to return {0}.");
private final AbstractCompiler compiler;
private final CheckLevel level;
private static final Predicate<Node> IS_RETURN = new Predicate<Node>() {
@Override
public boolean apply(Node input) {
// Check for null because the control flow graph's implicit return node is
// represented by null, so this value might be input.
return input != null && input.isReturn();
}
};
/* Skips all exception edges and impossible edges. */
private static final Predicate<DiGraphEdge<Node, ControlFlowGraph.Branch>>
GOES_THROUGH_TRUE_CONDITION_PREDICATE =
new Predicate<DiGraphEdge<Node, ControlFlowGraph.Branch>>() {
@Override
public boolean apply(DiGraphEdge<Node, ControlFlowGraph.Branch> input) {
// First skill all exceptions.
Branch branch = input.getValue();
if (branch == Branch.ON_EX) {
return false;
} else if (branch.isConditional()) {
Node condition = NodeUtil.getConditionExpression(
input.getSource().getValue());
// TODO(user): We CAN make this bit smarter just looking at
// constants. We DO have a full blown ReverseAbstractInterupter and
// type system that can evaluate some impressions' boolean value but
// for now we will keep this pass lightweight.
if (condition != null) {
TernaryValue val = NodeUtil.getImpureBooleanValue(condition);
if (val != TernaryValue.UNKNOWN) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
return val.toBoolean(true) == (Branch.ON_TRUE == branch);
}
}
}
return true;
}
};
/**
* @param level level of severity to report when a missing return statement
* is discovered
*/
CheckMissingReturn(AbstractCompiler compiler, CheckLevel level) {
this.compiler = compiler;
this.level = level;
}
@Override
public void enterScope(NodeTraversal t) {
JSType returnType = explicitReturnExpected(t.getScopeRoot());
if (returnType == null) {
return;
}
if (fastAllPathsReturnCheck(t.getControlFlowGraph())) {
return;
}
CheckPathsBetweenNodes<Node, ControlFlowGraph.Branch> test =
new CheckPathsBetweenNodes<Node, ControlFlowGraph.Branch>(
t.getControlFlowGraph(),
t.getControlFlowGraph().getEntry(),
t.getControlFlowGraph().getImplicitReturn(),
IS_RETURN, GOES_THROUGH_TRUE_CONDITION_PREDICATE);
if (!test.allPathsSatisfyPredicate()) {
compiler.report(
t.makeError(t.getScopeRoot(), level,
MISSING_RETURN_STATEMENT, returnType.toString()));
}
}
/**
* Fast check to see if all execution paths contain a return statement.
* May spuriously report that a return statement is missing.
*
* @return true if all paths return, converse not necessarily true
*/
private static boolean fastAllPathsReturnCheck(ControlFlowGraph<Node> cfg) {
for (DiGraphEdge<Node, Branch> s : cfg.getImplicitReturn().getInEdges()) {
if (!s.getSource().getValue().isReturn()) {
return false;
}
}
return true;
}
@Override
public void exitScope(NodeTraversal t) {
}
@Override
public boolean shouldTraverse(
NodeTraversal nodeTraversal, Node n, Node parent) {
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
}
/**
* Determines if the given scope should explicitly return. All functions
* with non-void or non-unknown return types must have explicit returns.
*
* Exception: Constructors which specifically specify a return type are
* used to allow invocation without requiring the "new" keyword. They
* have an implicit return type. See unit tests.
*
* @return If a return type is expected, returns it. Otherwise, returns null.
*/
private JSType explicitReturnExpected(Node scope) {
FunctionType scopeType = JSType.toMaybeFunctionType(scope.getJSType());
if (scopeType == null) {
return null;
}
if (isEmptyFunction(scope)) {
return null;
}
if (scopeType.isConstructor()) {
return null;
}
JSType returnType = scopeType.getReturnType();
if (returnType == null) {
return null;
}
if (!isVoidOrUnknown(returnType)) {
return returnType;
}
return null;
}
/**
* @return {@code true} if function represents a JavaScript function
* with an empty body
*/
private static boolean isEmptyFunction(Node function) {
return function.getChildCount()
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.javascript.jscomp.Scope.Var;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.StaticSymbolTable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Memoize a scope creator.
*
* This allows you to make multiple passes, without worrying about
* the expense of generating Scope objects over and over again.
*
* On the other hand, you also have to be more aware of what your passes
* are doing. Scopes are memoized stupidly, so if the underlying tree
* changes, the scope may be out of sync.
*
* @author nicksantos@google.com (Nick Santos)
*/
class MemoizedScopeCreator
implements ScopeCreator, StaticSymbolTable<Var, Var> {
private final Map<Node, Scope> scopes = Maps.newHashMap();
private final ScopeCreator delegate;
/**
* @param delegate The real source of Scope objects.
*/
MemoizedScopeCreator(ScopeCreator delegate) {
this.delegate = delegate;
}
@Override
public Iterable<Var> getReferences(Var var) {
return ImmutableList.of(var);
}
@Override
public Scope getScope(Var var) {
return var.scope;
}
@Override
public Iterable<Var> getAllSymbols() {
List<Var> vars = Lists.newArrayList();
for (Scope s : scopes.values()) {
Iterables.addAll(vars, s.getAllSymbols());
}
return vars;
}
@Override
public Scope createScope(Node n, Scope parent) {
Scope scope = scopes.get(n);
if (scope == null) {
scope = delegate.createScope(n, parent);
scopes.put(n, scope);
} else {
Preconditions.checkState(parent == scope.getParent());
}
return scope;
}
Collection<Scope> getAllMemoizedScopes() {
return Collections.unmodifiableCollection(scopes.values());
}
Scope getScopeIfMemoized(Node n) {
return scopes.get(n
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.graph;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* A directed graph using linked list within nodes to store edge information.
* <p>
* This implementation favors directed graph operations inherited from <code>
* DirectedGraph</code>.
* Operations from <code>Graph</code> would tends to be slower.
*
*
* @param <N> Value type that the graph node stores.
* @param <E> Value type that the graph edge stores.
*/
public class LinkedDirectedGraph<N, E>
extends DiGraph<N, E> implements GraphvizGraph {
protected final Map<N, LinkedDirectedGraphNode<N, E>> nodes =
Maps.newHashMap();
@Override
public SubGraph<N, E> newSubGraph() {
return new SimpleSubGraph<N, E>(this);
}
public static <N, E> LinkedDirectedGraph<N, E> createWithoutAnnotations() {
return new LinkedDirectedGraph<N, E>(false, false);
}
public static <N, E> LinkedDirectedGraph<N, E> createWithNodeAnnotations() {
return new LinkedDirectedGraph<N, E>(true, false);
}
public static <N, E> LinkedDirectedGraph<N, E> createWithEdgeAnnotations() {
return new LinkedDirectedGraph<N, E>(false, true);
}
public static <N, E> LinkedDirectedGraph<N, E> create() {
return new LinkedDirectedGraph<N, E>(true, true);
}
private final boolean useNodeAnnotations;
private final boolean useEdgeAnnotations;
protected LinkedDirectedGraph(
boolean useNodeAnnotations, boolean useEdgeAnnotations) {
this.useNodeAnnotations = useNodeAnnotations;
this.useEdgeAnnotations = useEdgeAnnotations;
}
@Override
public void connect(N srcValue, E edgeValue, N destValue) {
LinkedDirectedGraphNode<N, E> src = getNodeOrFail(srcValue);
LinkedDirectedGraphNode<N, E> dest = getNodeOrFail(destValue);
LinkedDirectedGraphEdge<
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>N, E> edge =
useEdgeAnnotations ?
new AnnotatedLinkedDirectedGraphEdge<N, E>(src, edgeValue, dest) :
new LinkedDirectedGraphEdge<N, E>(src, edgeValue, dest);
src.getOutEdges().add(edge);
dest.getInEdges().add(edge);
}
@Override
public void disconnect(N n1, N n2) {
disconnectInDirection(n1, n2);
disconnectInDirection(n2, n1);
}
@Override
public void disconnectInDirection(N srcValue, N destValue) {
LinkedDirectedGraphNode<N, E> src = getNodeOrFail(srcValue);
LinkedDirectedGraphNode<N, E> dest = getNodeOrFail(destValue);
for (DiGraphEdge<?, E> edge : getDirectedGraphEdges(srcValue, destValue)) {
src.getOutEdges().remove(edge);
dest.getInEdges().remove(edge);
}
}
@Override
public Iterable<DiGraphNode<N, E>> getDirectedGraphNodes() {
return Collections.<DiGraphNode<N, E>>unmodifiableCollection(
nodes.values());
}
@Override
public DiGraphNode<N, E> getDirectedGraphNode(N nodeValue) {
return nodes.get(nodeValue);
}
@Override
public GraphNode<N, E> getNode(N nodeValue) {
return getDirectedGraphNode(nodeValue);
}
@Override
public List<DiGraphEdge<N, E>> getInEdges(N nodeValue) {
LinkedDirectedGraphNode<N, E> node = getNodeOrFail(nodeValue);
return Collections.<DiGraphEdge<N, E>>unmodifiableList(node.getInEdges());
}
@Override
public List<DiGraphEdge<N, E>> getOutEdges(N nodeValue) {
LinkedDirectedGraphNode<N, E> node = getNodeOrFail(nodeValue);
return Collections.<DiGraphEdge<N, E>>unmodifiableList(node.getOutEdges());
}
@Override
public DiGraphNode<N, E> createDirectedGraphNode(N nodeValue) {
LinkedDirectedGraphNode<N, E> node = nodes.get(nodeValue);
if (node == null) {
node = useNodeAnnotations ?
new AnnotatedLinkedDirectedGraphNode<N, E>(nodeValue) :
new LinkedDirectedGraphNode<N, E>(nodeValue);
nodes.put(nodeValue, node);
}
return node;
}
@Override
public List<DiGraphEdge<N, E>> getEdges(N n1, N n2) {
// Since this is a method from a generic graph, edges from both
// directions must be added to the returning list.
List<DiGraphEdge<N, E>> forwardEdges = getDirectedGraphEdges(n1, n2);
List<DiGraphEdge<N, E>> backwardEdges = getDirectedGraphEdges(n2, n1);
int totalSize = forwardEdges.size() + backwardEdges.size();
List<DiGraphEdge<N, E>> edges = Lists.newArrayListWithCapacity(totalSize);
edges.addAll(forwardEdges);
edges.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>addAll(backwardEdges);
return edges;
}
@Override
public GraphEdge<N, E> getFirstEdge(N n1, N n2) {
DiGraphNode<N, E> dNode1 = getNodeOrFail(n1);
DiGraphNode<N, E> dNode2 = getNodeOrFail(n2);
for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) {
if (outEdge.getDestination() == dNode2) {
return outEdge;
}
}
for (DiGraphEdge<N, E> outEdge : dNode2.getOutEdges()) {
if (outEdge.getDestination() == dNode1) {
return outEdge;
}
}
return null;
}
@Override
public GraphNode<N, E> createNode(N value) {
return createDirectedGraphNode(value);
}
@Override
public List<DiGraphEdge<N, E>> getDirectedGraphEdges(N n1, N n2) {
DiGraphNode<N, E> dNode1 = getNodeOrFail(n1);
DiGraphNode<N, E> dNode2 = getNodeOrFail(n2);
List<DiGraphEdge<N, E>> edges = Lists.newArrayList();
for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) {
if (outEdge.getDestination() == dNode2) {
edges.add(outEdge);
}
}
return edges;
}
@Override
public boolean isConnectedInDirection(N n1, N n2) {
return isConnectedInDirection(n1, Predicates.<E>alwaysTrue(), n2);
}
@Override
public boolean isConnectedInDirection(N n1, E edgeValue, N n2) {
return isConnectedInDirection(n1, Predicates.equalTo(edgeValue), n2);
}
private boolean isConnectedInDirection(N n1, Predicate<E> edgeMatcher, N n2) {
// Verify the nodes.
DiGraphNode<N, E> dNode1 = getNodeOrFail(n1);
DiGraphNode<N, E> dNode2 = getNodeOrFail(n2);
for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) {
if (outEdge.getDestination() == dNode2 &&
edgeMatcher.apply(outEdge.getValue())) {
return true;
}
}
return false;
}
@Override
public List<DiGraphNode<N, E>> getDirectedPredNodes(N nodeValue) {
return getDirectedPredNodes(nodes.get(nodeValue));
}
@Override
public List<DiGraphNode<N, E>> getDirectedSuccNodes(N nodeValue) {
return getDirectedSuccNodes(nodes.get(nodeValue));
}
@Override
public List<DiGraphNode<N, E>> getDirectedPredNodes(
DiGraphNode<N, E> dNode) {
if (dNode == null) {
throw new IllegalArgumentException(dNode + " is null");
}
List<DiGraphNode<N, E>> nodeList = Lists.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>newArrayList();
for (DiGraphEdge<N, E> edge : dNode.getInEdges()) {
nodeList.add(edge.getSource());
}
return nodeList;
}
@Override
public List<DiGraphNode<N, E>> getDirectedSuccNodes(
DiGraphNode<N, E> dNode) {
if (dNode == null) {
throw new IllegalArgumentException(dNode + " is null");
}
List<DiGraphNode<N, E>> nodeList = Lists.newArrayList();
for (DiGraphEdge<N, E> edge : dNode.getOutEdges()) {
nodeList.add(edge.getDestination());
}
return nodeList;
}
@Override
public List<GraphvizEdge> getGraphvizEdges() {
List<GraphvizEdge> edgeList = Lists.newArrayList();
for (LinkedDirectedGraphNode<N, E> node : nodes.values()) {
for (DiGraphEdge<N, E> edge : node.getOutEdges()) {
edgeList.add((LinkedDirectedGraphEdge<N, E>) edge);
}
}
return edgeList;
}
@Override
public List<GraphvizNode> getGraphvizNodes() {
List<GraphvizNode> nodeList =
Lists.newArrayListWithCapacity(nodes.size());
for (LinkedDirectedGraphNode<N, E> node : nodes.values()) {
nodeList.add(node);
}
return nodeList;
}
@Override
public String getName() {
return "LinkedGraph";
}
@Override
public boolean isDirected() {
return true;
}
@Override
public Collection<DiGraphNode<N, E>> getNodes() {
return Collections.<DiGraphNode<N, E>>unmodifiableCollection(nodes.values());
}
@Override
public List<GraphNode<N, E>> getNeighborNodes(N value) {
DiGraphNode<N, E> node = getDirectedGraphNode(value);
return getNeighborNodes(node);
}
public List<GraphNode<N, E>> getNeighborNodes(DiGraphNode<N, E> node) {
List<GraphNode<N, E>> result = Lists.newArrayList();
for (Iterator<GraphNode<N, E>> i =
((LinkedDirectedGraphNode<N, E>) node).neighborIterator(); i.hasNext();) {
result.add(i.next());
}
return result;
}
@Override
public Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value) {
LinkedDirectedGraphNode<N, E> node = nodes.get(value);
Preconditions.checkNotNull(node);
return node.neighborIterator();
}
@Override
public List<DiGraphEdge<N, E>> getEdges() {
List<DiGraphEdge<N, E>> result = Lists.newArrayList();
for (DiGraphNode<N, E> node : nodes.values()) {
for (DiGraphEdge<N, E> edge : node.getOutEdges()) {
result.add(edge);
}
}
return Collections.unmodifiableList(result);
}
@Override
public int getNodeDegree(N value) {
DiGraphNode<N,
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> E> node = getNodeOrFail(value);
return node.getInEdges().size() + node.getOutEdges().size();
}
/**
* A directed graph node that stores outgoing edges and incoming edges as an
* list within the node itself.
*/
static class LinkedDirectedGraphNode<N, E> implements DiGraphNode<N, E>,
GraphvizNode {
List<DiGraphEdge<N, E>> inEdgeList = Lists.newArrayList();
List<DiGraphEdge<N, E>> outEdgeList =
Lists.newArrayList();
protected final N value;
/**
* Constructor
*
* @param nodeValue Node's value.
*/
LinkedDirectedGraphNode(N nodeValue) {
this.value = nodeValue;
}
@Override
public N getValue() {
return value;
}
@Override
public <A extends Annotation> A getAnnotation() {
throw new UnsupportedOperationException(
"Graph initialized with node annotations turned off");
}
@Override
public void setAnnotation(Annotation data) {
throw new UnsupportedOperationException(
"Graph initialized with node annotations turned off");
}
@Override
public String getColor() {
return "white";
}
@Override
public String getId() {
return "LDN" + hashCode();
}
@Override
public String getLabel() {
return value != null ? value.toString() : "null";
}
@Override
public String toString() {
return getLabel();
}
@Override
public List<DiGraphEdge<N, E>> getInEdges() {
return inEdgeList;
}
@Override
public List<DiGraphEdge<N, E>> getOutEdges() {
return outEdgeList;
}
private Iterator<GraphNode<N, E>> neighborIterator() {
return new NeighborIterator();
}
private class NeighborIterator implements Iterator<GraphNode<N, E>> {
private final Iterator<DiGraphEdge<N, E>> in = inEdgeList.iterator();
private final Iterator<DiGraphEdge<N, E>> out = outEdgeList.iterator();
@Override
public boolean hasNext() {
return in.hasNext() || out.hasNext();
}
@Override
public GraphNode<N, E> next() {
boolean isOut = !in.hasNext();
Iterator<DiGraphEdge<N, E>> curIterator = isOut ? out : in;
DiGraphEdge<N, E> s = curIterator.next();
return isOut ? s.getDestination() : s.getSource();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove not supported.");
}
}
}
/**
* A directed graph node with annotations.
*/
static class AnnotatedLinkedDirectedGraphNode<N, E>
extends LinkedDirectedGraphNode<N, E> {
protected Annotation annotation;
/**
* @param nodeValue Node's value.
*/
AnnotatedLinkedDirectedGraphNode(N nodeValue) {
super(nodeValue);
}
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A getAnnotation() {
return (A) annotation;
}
@Override
public void setAnnotation(Annotation data) {
annotation = data;
}
}
/**
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> * A directed graph edge that stores the source and destination nodes at each
* edge.
*/
static class LinkedDirectedGraphEdge<N, E> implements DiGraphEdge<N, E>,
GraphvizEdge {
private DiGraphNode<N, E> sourceNode;
private DiGraphNode<N, E> destNode;
protected final E value;
/**
* Constructor.
*
* @param edgeValue Edge Value.
*/
LinkedDirectedGraphEdge(DiGraphNode<N, E> sourceNode,
E edgeValue, DiGraphNode<N, E> destNode) {
this.value = edgeValue;
this.sourceNode = sourceNode;
this.destNode = destNode;
}
@Override
public DiGraphNode<N, E> getSource() {
return sourceNode;
}
@Override
public DiGraphNode<N, E> getDestination() {
return destNode;
}
@Override
public void setDestination(DiGraphNode<N, E> node) {
destNode = node;
}
@Override
public void setSource(DiGraphNode<N, E> node) {
sourceNode = node;
}
@Override
public E getValue() {
return value;
}
@Override
public <A extends Annotation> A getAnnotation() {
throw new UnsupportedOperationException(
"Graph initialized with edge annotations turned off");
}
@Override
public void setAnnotation(Annotation data) {
throw new UnsupportedOperationException(
"Graph initialized with edge annotations turned off");
}
@Override
public String getColor() {
return "black";
}
@Override
public String getLabel() {
return value != null ? value.toString() : "null";
}
@Override
public String getNode1Id() {
return ((LinkedDirectedGraphNode<N, E>) sourceNode).getId();
}
@Override
public String getNode2Id() {
return ((LinkedDirectedGraphNode<N, E>) destNode).getId();
}
@Override
public String toString() {
return sourceNode.toString() + " -> " + destNode.toString();
}
@Override
public GraphNode<N, E> getNodeA() {
return sourceNode;
}
@Override
public GraphNode<N, E> getNodeB() {
return destNode;
}
}
/**
* A directed graph edge that stores the source and destination nodes at each
* edge.
*/
static class AnnotatedLinkedDirectedGraphEdge<N, E>
extends LinkedDirectedGraphEdge<N, E> {
protected Annotation annotation;
/**
* Constructor.
*
* @param edgeValue Edge Value.
*/
AnnotatedLinkedDirectedGraphEdge(DiGraphNode<N, E> sourceNode,
E edgeValue, DiGraphNode<N, E> destNode) {
super(sourceNode, edgeValue, destNode);
}
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A getAnnotation() {
return (A) annotation;
}
@Override
public void setAnnotation(Annotation data) {
annotation = data;
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* Prepare the AST before we do any checks or optimizations on it.
*
* This pass must run. It should bring the AST into a consistent state,
* and add annotations where necessary. It should not make any transformations
* on the tree that would lose source information, since we need that source
* information for checks.
*
* @author johnlenz@google.com (John Lenz)
*/
class PrepareAst implements CompilerPass {
private final AbstractCompiler compiler;
private final boolean checkOnly;
PrepareAst(AbstractCompiler compiler) {
this(compiler, false);
}
PrepareAst(AbstractCompiler compiler, boolean checkOnly) {
this.compiler = compiler;
this.checkOnly = checkOnly;
}
private void reportChange() {
if (checkOnly) {
Preconditions.checkState(false, "normalizeNodeType constraints violated");
}
}
@Override
public void process(Node externs, Node root) {
if (checkOnly) {
normalizeNodeTypes(root);
} else {
// Don't perform "PrepareAnnotations" when doing checks as
// they currently aren't valid during sanity checks. In particular,
// they DIRECT_EVAL shouldn't be applied after inlining has been
// performed.
if (externs != null) {
NodeTraversal.traverse(
compiler, externs, new PrepareAnnotations());
}
if (root != null) {
NodeTraversal.traverse(
compiler, root, new PrepareAnnotations());
}
}
}
/**
* Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code.
*/
private void normalizeNodeTypes(Node n) {
normalizeBlocks(n);
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
// This pass is run during the CompilerTestCase validation, so this
// parent pointer check serves as a more general check.
Preconditions.checkState(child.getParent() == n);
normalizeNodeTypes(child);
}
}
/**
* Add blocks to IF, WHILE, DO, etc.
*/
private void normalizeBlocks(Node n) {
if (NodeUtil.isControlStructure(n)
&& !n.isLabel()
&& !n.isSwitch()) {
for (Node c = n.getFirstChild(); c
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> != null; c = c.getNext()) {
if (NodeUtil.isControlStructureCodeBlock(n, c) && !c.isBlock()) {
Node newBlock = IR.block().srcref(n);
n.replaceChild(c, newBlock);
if (!c.isEmpty()) {
newBlock.addChildrenToFront(c);
} else {
newBlock.setWasEmptyNode(true);
}
c = newBlock;
reportChange();
}
}
}
}
/**
* Normalize where annotations appear on the AST. Copies
* around existing JSDoc annotations as well as internal annotations.
*/
static class PrepareAnnotations
implements NodeTraversal.Callback {
PrepareAnnotations() {
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.isObjectLit()) {
normalizeObjectLiteralAnnotations(n);
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.CALL:
annotateCalls(n);
break;
case Token.FUNCTION:
annotateDispatchers(n, parent);
break;
}
}
private void normalizeObjectLiteralAnnotations(Node objlit) {
Preconditions.checkState(objlit.isObjectLit());
for (Node key = objlit.getFirstChild();
key != null; key = key.getNext()) {
Node value = key.getFirstChild();
normalizeObjectLiteralKeyAnnotations(objlit, key, value);
}
}
/**
* There are two types of calls we are interested in calls without explicit
* "this" values (what we are call "free" calls) and direct call to eval.
*/
private void annotateCalls(Node n) {
Preconditions.checkState(n.isCall());
// Keep track of of the "this" context of a call. A call without an
// explicit "this" is a free call.
Node first = n.getFirstChild();
// ignore cast nodes.
while (first.isCast()) {
first = first.getFirstChild();
}
if (!NodeUtil.isGet(first)) {
n.putBooleanProp(Node.FREE_CALL, true);
}
// Keep track of the context in which eval is called. It is important
// to distinguish between "(0, eval)()" and "eval()".
if (first.isName() &&
"eval".equals(first.getString())) {
first.putBooleanProp(Node.DIRECT_EVAL, true);
}
}
/**
* Translate dispatcher info into the property expected node.
*/
private void annotateDispatchers(Node n, Node parent) {
Preconditions.checkState(n.isFunction());
if (parent.getJSDocInfo() != null
&& parent.getJSDocInfo().isJavaDispatch()) {
if (parent.isAssign()) {
Preconditions.checkState(parent.getLastChild() == n);
n.putBooleanProp(Node.IS_DISPATCHER, true);
}
}
}
/**
* In the AST that Rhino gives us, it needs to make a distinction
* between JsDoc on the object literal node and JsDoc on the object literal
* value
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>. For example,
* <pre>
* var x = {
* / JSDOC /
* a: 'b',
* c: / JSDOC / 'd'
* };
* </pre>
*
* But in few narrow cases (in particular, function literals), it's
* a lot easier for us if the doc is attached to the value.
*/
private void normalizeObjectLiteralKeyAnnotations(
Node objlit, Node key, Node value) {
Preconditions.checkState(objlit.isObjectLit());
if (key.getJSDocInfo() != null &&
value.isFunction()) {
value.setJSDocInfo(key.getJSDocInfo());
}
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> JSType newType) {
Var newVar = var;
boolean allowDupe = false;
if (n.isGetProp() ||
NodeUtil.isObjectLitKey(n)) {
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
info = parent.getJSDocInfo();
}
allowDupe =
info != null && info.getSuppressions().contains("duplicate");
}
JSType varType = var.getType();
// Only report duplicate declarations that have types. Other duplicates
// will be reported by the syntactic scope creator later in the
// compilation process.
if (varType != null &&
varType != typeRegistry.getNativeType(UNKNOWN_TYPE) &&
newType != null &&
newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) {
// If there are two typed declarations of the same variable, that
// is an error and the second declaration is ignored, except in the
// case of native types. A null input type means that the declaration
// was made in TypedScopeCreator#createInitialScope and is a
// native type. We should redeclare it at the new input site.
if (var.input == null) {
Scope s = var.getScope();
s.undeclare(var);
newVar = s.declare(variableName, n, varType, input, false);
n.setJSType(varType);
if (parent.isVar()) {
if (n.getFirstChild() != null) {
n.getFirstChild().setJSType(varType);
}
} else {
Preconditions.checkState(parent.isFunction());
parent.setJSType(varType);
}
} else {
// Always warn about duplicates if the overridden type does not
// match the original type.
//
// If the types match, suppress the warning iff there was a @suppress
// tag, or if the original declaration was a stub.
if (!(allowDupe ||
var.getParentNode().isExprResult()) ||
!newType.isEquivalentTo(varType)) {
report(JSError.make(sourceName, n, DUP_VAR_DECLARATION,
variableName, newType.toString(), var.getInputName(),
String.valueOf(var.nameNode.getLineno()),
varType.toString()));
}
}
}
return newVar;
}
/**
* Expect that all properties on interfaces that this type implements are
* implemented and correctly typed.
*/
void expectAllInterfaceProperties(NodeTraversal t, Node n,
FunctionType type) {
ObjectType instance = type.getInstanceType();
for (ObjectType implemented : type.getAllImplementedInterfaces()) {
if (implemented.getImplicitPrototype() != null) {
for (String prop :
implemented.getImplicitPrototype().getOwnPropertyNames()) {
expectInterfaceProperty(t, n, instance, implemented, prop);
}
}
}
}
/**
* Expect that the property in an interface that this type implements is
* implemented and correctly typed.
*/
private void expectInterfaceProperty(NodeTraversal t, Node n,
ObjectType instance, ObjectType implementedInterface, String prop) {
StaticSlot<JSType> propSlot = instance.getSlot(prop);
if (propSlot ==
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> null) {
// TODO(user): This branch indicates a compiler bug, not worthy of
// halting the compilation but we should log this and analyze to track
// down why it happens. This is not critical and will be resolved over
// time as the type checker is extended.
return getNativeType(UNKNOWN_TYPE);
} else {
return jsType;
}
}
private JSType getNativeType(JSTypeNative typeId) {
return typeRegistry.getNativeType(typeId);
}
private JSError report(JSError error) {
if (shouldReport) {
compiler.report(error);
}
return error;
}
/**
* Signals that the first type and the second type have been
* used interchangeably.
*
* Type-based optimizations should take this into account
* so that they don't wreck code with type warnings.
*/
static class TypeMismatch {
final JSType typeA;
final JSType typeB;
final JSError src;
/**
* It's the responsibility of the class that creates the
* {@code TypeMismatch} to ensure that {@code a} and {@code b} are
* non-matching types.
*/
TypeMismatch(JSType a, JSType b, JSError src) {
this.typeA = a;
this.typeB = b;
this.src = src;
}
@Override public boolean equals(Object object) {
if (object instanceof TypeMismatch) {
TypeMismatch that = (TypeMismatch) object;
return (that.typeA.isEquivalentTo(this.typeA)
&& that.typeB.isEquivalentTo(this.typeB))
|| (that.typeB.isEquivalentTo(this.typeA)
&& that.typeA.isEquivalentTo(this.typeB));
}
return false;
}
@Override public int hashCode() {
return Objects.hashCode(typeA, typeB);
}
@Override public String toString() {
return "(" + typeA + ", " + typeB + ")";
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.type;
import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE;
import com.google.common.base.Function;
import com.google.javascript.jscomp.CodingConvention;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSType.TypePair;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.jstype.StaticSlot;
import com.google.javascript.rhino.jstype.UnionType;
import com.google.javascript.rhino.jstype.Visitor;
/**
* A reverse abstract interpreter using the semantics of the JavaScript
* language as a means to reverse interpret computations. This interpreter
* expects the parse tree inputs to be typed.
*
*/
public class SemanticReverseAbstractInterpreter
extends ChainableReverseAbstractInterpreter {
/**
* Merging function for equality between types.
*/
private static final Function<TypePair, TypePair> EQ =
new Function<TypePair, TypePair>() {
@Override
public TypePair apply(TypePair p) {
if (p.typeA == null || p.typeB == null) {
return null;
}
return p.typeA.getTypesUnderEquality(p.typeB);
}
};
/**
* Merging function for non-equality between types.
*/
private static final Function<TypePair, TypePair> NE =
new Function<TypePair, TypePair>() {
@Override
public TypePair apply(TypePair p) {
if (p.typeA == null || p.typeB == null) {
return null;
}
return p.typeA.getTypesUnderInequality(p.typeB);
}
};
/**
* Merging function for strict equality between types.
*/
private static final
Function<TypePair, TypePair> SHEQ =
new Function<TypePair, TypePair>() {
@Override
public TypePair apply(TypePair p) {
if (p.typeA == null || p.typeB == null) {
return null;
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
}
return p.typeA.getTypesUnderShallowEquality(p.typeB);
}
};
/**
* Merging function for strict non-equality between types.
*/
private static final
Function<TypePair, TypePair> SHNE =
new Function<TypePair, TypePair>() {
@Override
public TypePair apply(TypePair p) {
if (p.typeA == null || p.typeB == null) {
return null;
}
return p.typeA.getTypesUnderShallowInequality(p.typeB);
}
};
/**
* Merging function for inequality comparisons between types.
*/
private final Function<TypePair, TypePair> ineq =
new Function<TypePair, TypePair>() {
@Override
public TypePair apply(TypePair p) {
return new TypePair(
getRestrictedWithoutUndefined(p.typeA),
getRestrictedWithoutUndefined(p.typeB));
}
};
/**
* Creates a semantic reverse abstract interpreter.
*/
public SemanticReverseAbstractInterpreter(CodingConvention convention,
JSTypeRegistry typeRegistry) {
super(convention, typeRegistry);
}
@Override
public FlowScope getPreciserScopeKnowingConditionOutcome(Node condition,
FlowScope blindScope, boolean outcome) {
// Check for the typeof operator.
int operatorToken = condition.getType();
switch (operatorToken) {
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE:
case Token.CASE:
Node left;
Node right;
if (operatorToken == Token.CASE) {
left = condition.getParent().getFirstChild(); // the switch condition
right = condition.getFirstChild();
} else {
left = condition.getFirstChild();
right = condition.getLastChild();
}
Node typeOfNode = null;
Node stringNode = null;
if (left.isTypeOf() && right.isString()) {
typeOfNode = left;
stringNode = right;
} else if (right.isTypeOf() &&
left.isString()) {
typeOfNode = right;
stringNode = left;
}
if (typeOfNode != null && stringNode != null) {
Node operandNode = typeOfNode.getFirstChild();
JSType operandType = getTypeIfRefinable(operandNode, blindScope);
if (operandType != null) {
boolean resultEqualsValue = operatorToken == Token.EQ ||
operatorToken == Token.SHEQ || operatorToken == Token.CASE;
if (!outcome) {
resultEqualsValue = !resultEqualsValue;
}
return caseTypeOf(operandNode, operandType, stringNode.getString(),
resultEqualsValue, blindScope);
}
}
}
switch (operatorToken) {
case Token.AND:
if (outcome) {
return caseAndOrNotShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, true);
} else {
return caseAndOrMaybeShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, true);
}
case Token.OR:
if (!outcome) {
return caseAndOrNotShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, false);
} else
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>IsRefineable = true;
} else {
rightIsRefineable = false;
rightType = right.getJSType();
}
// merged types
TypePair merged = merging.apply(new TypePair(leftType, rightType));
// creating new scope
if (merged != null) {
return maybeRestrictTwoNames(
blindScope,
left, leftType, leftIsRefineable ? merged.typeA : null,
right, rightType, rightIsRefineable ? merged.typeB : null);
}
return blindScope;
}
private FlowScope caseAndOrNotShortCircuiting(Node left, Node right,
FlowScope blindScope, boolean condition) {
// left type
JSType leftType = getTypeIfRefinable(left, blindScope);
boolean leftIsRefineable;
if (leftType != null) {
leftIsRefineable = true;
} else {
leftIsRefineable = false;
leftType = left.getJSType();
blindScope = firstPreciserScopeKnowingConditionOutcome(
left, blindScope, condition);
}
// restricting left type
JSType restrictedLeftType = (leftType == null) ? null :
leftType.getRestrictedTypeGivenToBooleanOutcome(condition);
if (restrictedLeftType == null) {
return firstPreciserScopeKnowingConditionOutcome(
right, blindScope, condition);
}
// right type
JSType rightType = getTypeIfRefinable(right, blindScope);
boolean rightIsRefineable;
if (rightType != null) {
rightIsRefineable = true;
} else {
rightIsRefineable = false;
rightType = right.getJSType();
blindScope = firstPreciserScopeKnowingConditionOutcome(
right, blindScope, condition);
}
if (condition) {
JSType restrictedRightType = (rightType == null) ? null :
rightType.getRestrictedTypeGivenToBooleanOutcome(condition);
// creating new scope
return maybeRestrictTwoNames(
blindScope,
left, leftType, leftIsRefineable ? restrictedLeftType : null,
right, rightType, rightIsRefineable ? restrictedRightType : null);
}
return blindScope;
}
private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right,
FlowScope blindScope, boolean condition) {
FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome(
left, blindScope, !condition);
StaticSlot<JSType> leftVar = leftScope.findUniqueRefinedSlot(blindScope);
if (leftVar == null) {
// If we did create a more precise scope, blindScope has a child and
// it is frozen. We can't just throw it away to return it. So we
// must create a child instead.
return blindScope == leftScope ?
blindScope : blindScope.createChildFlowScope();
}
FlowScope rightScope = firstPreciserScopeKnowingConditionOutcome(
left, blindScope, condition);
rightScope = firstPreciserScopeKnowingConditionOutcome(
right, rightScope, !condition);
StaticSlot<JSType> rightVar = rightScope.findUniqueRefinedSlot(blindScope);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Refinable(left, blindScope);
if (leftType == null) {
return blindScope;
}
JSType rightType = right.getJSType();
ObjectType targetType =
typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
if (rightType != null && rightType.isFunctionType()) {
targetType = rightType.toMaybeFunctionType();
}
Visitor<JSType> visitor;
if (outcome) {
visitor = new RestrictByTrueInstanceOfResultVisitor(targetType);
} else {
visitor = new RestrictByFalseInstanceOfResultVisitor(targetType);
}
return maybeRestrictName(
blindScope, left, leftType, leftType.visit(visitor));
}
/**
* Given 'property in object', ensures that the object has the property in the
* informed scope by defining it as a qualified name if the object type lacks
* the property and it's not in the blind scope.
* @param object The node of the right-side of the in.
* @param propertyName The string of the left-side of the in.
*/
private FlowScope caseIn(Node object, String propertyName, FlowScope blindScope) {
JSType jsType = object.getJSType();
jsType = this.getRestrictedWithoutNull(jsType);
jsType = this.getRestrictedWithoutUndefined(jsType);
boolean hasProperty = false;
ObjectType objectType = ObjectType.cast(jsType);
if (objectType != null) {
hasProperty = objectType.hasProperty(propertyName);
}
if (!hasProperty) {
String qualifiedName = object.getQualifiedName();
if (qualifiedName != null) {
String propertyQualifiedName = qualifiedName + "." + propertyName;
if (blindScope.getSlot(propertyQualifiedName) == null) {
FlowScope informed = blindScope.createChildFlowScope();
JSType unknownType = typeRegistry.getNativeType(
JSTypeNative.UNKNOWN_TYPE);
informed.inferQualifiedSlot(
object, propertyQualifiedName, unknownType, unknownType);
return informed;
}
}
}
return blindScope;
}
/**
* @see SemanticReverseAbstractInterpreter#caseInstanceOf
*/
private class RestrictByTrueInstanceOfResultVisitor
extends RestrictByTrueTypeOfResultVisitor {
private final ObjectType target;
RestrictByTrueInstanceOfResultVisitor(ObjectType target) {
this.target = target;
}
@Override
protected JSType caseTopType(JSType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseUnknownType() {
FunctionType funcTarget = JSType.toMaybeFunctionType(target);
if (funcTarget != null && funcTarget.hasInstanceType()) {
return funcTarget.getInstanceType();
}
return getNativeType(UNKNOWN_TYPE);
}
@Override
public JSType caseObjectType(ObjectType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseUnionType(UnionType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseFunctionType(FunctionType type) {
return caseObjectType(type);
}
private JSType applyCommonRestriction(JSType type) {
if (target.isUnknownType()) {
return type;
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
}
FunctionType funcTarget = target.toMaybeFunctionType();
if (funcTarget.hasInstanceType()) {
return type.getGreatestSubtype(funcTarget.getInstanceType());
}
return null;
}
}
/**
* @see SemanticReverseAbstractInterpreter#caseInstanceOf
*/
private class RestrictByFalseInstanceOfResultVisitor
extends RestrictByFalseTypeOfResultVisitor {
private final ObjectType target;
RestrictByFalseInstanceOfResultVisitor(ObjectType target) {
this.target = target;
}
@Override
public JSType caseObjectType(ObjectType type) {
if (target.isUnknownType()) {
return type;
}
FunctionType funcTarget = target.toMaybeFunctionType();
if (funcTarget.hasInstanceType()) {
if (type.isSubtype(funcTarget.getInstanceType())) {
return null;
}
return type;
}
return null;
}
@Override
public JSType caseUnionType(UnionType type) {
if (target.isUnknownType()) {
return type;
}
FunctionType funcTarget = target.toMaybeFunctionType();
if (funcTarget.hasInstanceType()) {
return type.getRestrictedUnion(funcTarget.getInstanceType());
}
return null;
}
@Override
public JSType caseFunctionType(FunctionType type) {
return caseObjectType(type);
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2010 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.graph.LatticeElement;
import java.util.List;
/**
* Defines a way join a list of LatticeElements.
*/
interface JoinOp<L extends LatticeElement> extends Function<List<L>, L> {
/**
* An implementation of {@code JoinOp} that makes it easy to join to
* lattice elements at a time.
*/
abstract static class BinaryJoinOp<L extends LatticeElement>
implements JoinOp<L> {
@Override
public final L apply(List<L> values) {
Preconditions.checkArgument(!values.isEmpty());
int size = values.size();
if (size == 1) {
return values.get(0);
} else if (size == 2) {
return apply(values.get(0), values.get(1));
} else {
int mid = computeMidPoint(size);
return apply(
apply(values.subList(0, mid)),
apply(values.subList(mid, size)));
}
}
/**
* Creates a new lattice that will be the join of two input lattices.
*
* @return The join of {@code latticeA} and {@code latticeB}.
*/
abstract L apply(L latticeA, L latticeB);
/**
* Finds the midpoint of a list. The function will favor two lists of
* even length instead of two lists of the same odd length. The list
* must be at least length two.
*
* @param size Size of the list.
*/
static int computeMidPoint(int size) {
int midpoint = size >>> 1;
if (size > 4) {
/* Any list longer than 4 should prefer an even split point
* over the true midpoint, so that [0,6] splits at 2, not 3. */
midpoint &= -2; // (0xfffffffe) clears low bit so midpoint is even
}
return midpoint;
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
/**
* For functions with function(this: T, ...) and T as arguments, type inference
* will set the type of this on a function literal argument to the actual type
* of T.
*
*/
package com.google.javascript.rhino.jstype;
public class TemplateType extends ProxyObjectType {
private static final long serialVersionUID = 1L;
private final String name;
TemplateType(JSTypeRegistry registry, String name) {
super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE));
this.name = name;
}
@Override
public String getReferenceName() {
return name;
}
@Override
String toStringHelper(boolean forAnnotations) {
return name;
}
@Override
public TemplateType toMaybeTemplateType() {
return this;
}
@Override
public boolean hasAnyTemplateTypesInternal() {
return true;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseTemplateType(this);
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseTemplateType(this, that);
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.NodeTraversal.Callback;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* Checks for certain uses of the {@code this} keyword that are considered
* unsafe because they are likely to reference the global {@code this} object
* unintentionally.
*
* <p>A use of {@code this} is considered unsafe if it's on the left side of an
* assignment or a property access, and not inside one of the following:
* <ol>
* <li>a prototype method
* <li>a function annotated with {@code @constructor}
* <li>a function annotated with {@code @this}.
* <li>a function where there's no logical place to put a
* {@code this} annotation.
* </ol>
*
* <p>Note that this check does not track assignments of {@code this} to
* variables or objects. The code
* <pre>
* function evil() {
* var a = this;
* a.useful = undefined;
* }
* </pre>
* will not get flagged, even though it is semantically equivalent to
* <pre>
* function evil() {
* this.useful = undefined;
* }
* </pre>
* which would get flagged.
*
*/
final class CheckGlobalThis implements Callback {
static final DiagnosticType GLOBAL_THIS = DiagnosticType.warning(
"JSC_USED_GLOBAL_THIS",
"dangerous use of the global 'this' object");
private final AbstractCompiler compiler;
/**
* If {@code assignLhsChild != null}, then the node being traversed is
* a descendant of the first child of an ASSIGN node. assignLhsChild's
* parent is this ASSIGN node.
*/
private Node assignLhsChild = null;
CheckGlobalThis(AbstractCompiler compiler) {
this.compiler = compiler;
}
/**
* Since this pass reports errors only when a global {@code this} keyword
* is encountered, there is no reason to traverse non global contexts.
*/
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.isFunction()) {
// Don't traverse functions that are constructors or have the @this
// or @override annotation.
JSDocInfo jsDoc = getFunctionJsDocInfo(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>n);
if (jsDoc != null &&
(jsDoc.isConstructor() ||
jsDoc.isInterface() ||
jsDoc.hasThisType() ||
jsDoc.isOverride())) {
return false;
}
// Don't traverse functions unless they would normally
// be able to have a @this annotation associated with them. e.g.,
// var a = function() { }; // or
// function a() {} // or
// a.x = function() {}; // or
// var a = {x: function() {}};
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN ||
// object literal keys
pType == Token.STRING_KEY)) {
return false;
}
// Don't traverse functions that are getting lent to a prototype.
Node gramps = parent.getParent();
if (NodeUtil.isObjectLitKey(parent)) {
JSDocInfo maybeLends = gramps.getJSDocInfo();
if (maybeLends != null &&
maybeLends.getLendsName() != null &&
maybeLends.getLendsName().endsWith(".prototype")) {
return false;
}
}
}
if (parent != null && parent.isAssign()) {
Node lhs = parent.getFirstChild();
if (n == lhs) {
// Always traverse the left side of the assignment. To handle
// nested assignments properly (e.g., (a = this).property = c;),
// assignLhsChild should not be overridden.
if (assignLhsChild == null) {
assignLhsChild = lhs;
}
} else {
// Only traverse the right side if it's not an assignment to a prototype
// property or subproperty.
if (NodeUtil.isGet(lhs)) {
if (lhs.isGetProp() &&
lhs.getLastChild().getString().equals("prototype")) {
return false;
}
Node llhs = lhs.getFirstChild();
if (llhs.isGetProp() &&
llhs.getLastChild().getString().equals("prototype")) {
return false;
}
}
}
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isThis() && shouldReportThis(n)) {
compiler.report(t.makeError(n, GLOBAL_THIS));
}
if (n == assignLhsChild) {
assignLhsChild = null;
}
}
private boolean shouldReportThis(Node n) {
Node parent = n.getParent();
if (assignLhsChild != null) {
// Always report a THIS on the left side of an assign.
return true;
}
// Also report a THIS with a property access.
return parent != null && NodeUtil.isGet(parent);
}
/**
* Gets a function's JSDoc information, if it has any. Checks for a few
* patterns (ellipses show where JSDoc would be):
* <pre>
* ... function() {}
* ... x = function() {};
* var ... x = function() {};
* ... var x
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.collect.Maps;
import com.google.javascript.rhino.Node;
import java.util.Collections;
import java.util.HashMap;
/**
* A builder for record types.
*
*/
public class RecordTypeBuilder {
private boolean isEmpty = true;
private boolean isDeclared = true;
private final JSTypeRegistry registry;
private final HashMap<String, RecordProperty> properties = Maps.newHashMap();
public RecordTypeBuilder(JSTypeRegistry registry) {
this.registry = registry;
}
/** See the comments on RecordType about synthetic types. */
void setSynthesized(boolean synthesized) {
isDeclared = !synthesized;
}
/**
* Adds a property with the given name and type to the record type.
* @param name the name of the new property
* @param type the JSType of the new property
* @param propertyNode the node that holds this property definition
* @return The builder itself for chaining purposes, or null if there's
* a duplicate.
*/
public RecordTypeBuilder addProperty(String name, JSType type, Node
propertyNode) {
isEmpty = false;
if (properties.containsKey(name)) {
return null;
}
properties.put(name, new RecordProperty(type, propertyNode));
return this;
}
/**
* Creates a record.
* @
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
sourceName, line, lineOffset, type, message);
}
return JSError.make(sourceName, line, lineOffset, defaultLevel,
PARSE_ERROR, message);
}
private static class OldRhinoErrorReporter extends RhinoErrorReporter
implements ErrorReporter {
private OldRhinoErrorReporter(AbstractCompiler compiler) {
super(compiler);
}
@Override
public void error(String message, String sourceName, int line,
int lineOffset) {
super.errorAtLine(message, sourceName, line, lineOffset);
}
@Override
public void warning(String message, String sourceName, int line,
int lineOffset) {
super.warningAtLine(message, sourceName, line, lineOffset);
}
}
private static class NewRhinoErrorReporter extends RhinoErrorReporter
implements com.google.javascript.rhino.head.ast.IdeErrorReporter {
private NewRhinoErrorReporter(AbstractCompiler compiler) {
super(compiler);
}
@Override
public com.google.javascript.rhino.head.EvaluatorException
runtimeError(String message, String sourceName, int line,
String lineSource, int lineOffset) {
DiagnosticType type = mapError(message);
if (type != null) {
super.errorAtLine(message, sourceName, line, lineOffset);
}
return new com.google.javascript.rhino.head.EvaluatorException(
message, sourceName, line, lineSource, lineOffset);
}
@Override
public void error(String message, String sourceName, int line,
String sourceLine, int lineOffset) {
super.errorAtLine(message, sourceName, line, lineOffset);
}
@Override
public void error(String message, String sourceName,
int offset, int length) {
int line = 1;
int column = 0;
SourceFile file = this.compiler.getSourceFileByName(sourceName);
if (file != null) {
line = file.getLineOfOffset(offset);
column = file.getColumnOfOffset(offset);
}
super.errorAtLine(message, sourceName, line, column);
}
@Override
public void warning(String message, String sourceName, int line,
String sourceLine, int lineOffset) {
super.warningAtLine(message, sourceName, line, lineOffset);
}
@Override
public void warning(String message, String sourceName,
int offset, int length) {
int line = 1;
int column = 0;
SourceFile file = this.compiler.getSourceFileByName(sourceName);
if (file != null) {
line = file.getLineOfOffset(offset);
column = file.getColumnOfOffset(offset);
}
super.errorAtLine(message, sourceName, line, column);
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import java.io.Serializable;
import java.text.MessageFormat;
/**
* The type of a compile or analysis error.
*
*/
public class DiagnosticType
implements Comparable<DiagnosticType>, Serializable {
private static final long serialVersionUID = 1;
/**
* The error type. Used as the BugPattern and BugInstance types by
* BugBot's XML
*/
public final String key;
/** The default way to format errors */
public final MessageFormat format;
/** Default level */
public final CheckLevel defaultLevel;
/** Reporting level, initially the defaultLevel but may be changed. */
public CheckLevel level;
/**
* Create a DiagnosticType at level CheckLevel.ERROR
*
* @param name An identifier
* @param descriptionFormat A format string
* @return A new DiagnosticType
*/
public static DiagnosticType error(String name, String descriptionFormat) {
return make(name, CheckLevel.ERROR, descriptionFormat);
}
/**
* Create a DiagnosticType at level CheckLevel.WARNING
*
* @param name An identifier
* @param descriptionFormat A format string
* @return A new DiagnosticType
*/
public static DiagnosticType warning(String name, String descriptionFormat) {
return make(name, CheckLevel.WARNING, descriptionFormat);
}
/**
* Create a DiagnosticType at level CheckLevel.OFF
*
* @param name An identifier
* @param descriptionFormat A format string
* @return A new DiagnosticType
*/
public static DiagnosticType disabled(String name,
String descriptionFormat) {
return make(name, CheckLevel.OFF, descriptionFormat);
}
/**
* Create a DiagnosticType at a given CheckLevel.
*
* @param name An identifier
* @param level Either CheckLevel.ERROR or CheckLevel.WARNING
* @param descriptionFormat A format string
* @return A new DiagnosticType
*/
public static DiagnosticType make(String name, CheckLevel level,
String descriptionFormat) {
return
new DiagnosticType(name, level, new MessageFormat(descriptionFormat));
}
/**
* Create a DiagnosticType. Private to force use of static factory methods.
*/
private DiagnosticType(String key, CheckLevel level, MessageFormat format) {
this.key = key;
this.defaultLevel = level;
this.format = format;
this.level = this.defaultLevel;
}
/**
* Create a description from the MessageFormat and the arguments.
* Used by unit tests.
*/
String format(Object ... arguments) {
return format.format(arguments);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
}
@Override
public boolean equals(Object type) {
return type instanceof DiagnosticType &&
((DiagnosticType) type).key.equals(key);
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public int compareTo(DiagnosticType diagnosticType) {
return key.compareTo(diagnosticType.key);
}
@Override
public String toString() {
return key + ": " + format.toPattern();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* Boolean type.
*/
public class BooleanType extends ValueType {
private static final long serialVersionUID = 1L;
BooleanType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNullable() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isUnknownType() || that.isSubtype(
getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN)) ||
that.isObject()) {
return UNKNOWN;
}
return FALSE;
}
@Override
public boolean isBooleanValueType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
// TODO(user): Revisit this for ES4, which is stricter.
return true;
}
@Override
public JSType autoboxesTo() {
return getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>);
}
@Override
String toStringHelper(boolean forAnnotations) {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "boolean";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseBooleanType();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>GLOBAL_DEFINE_INIT_ERROR",
"@define variable {0} assignment must be global");
static final DiagnosticType DEFINE_NOT_ASSIGNABLE_ERROR =
DiagnosticType.error(
"JSC_DEFINE_NOT_ASSIGNABLE_ERROR",
"@define variable {0} cannot be reassigned due to code at {1}.");
private static final MessageFormat REASON_DEFINE_NOT_ASSIGNABLE =
new MessageFormat("line {0} of {1}");
/**
* Create a pass that overrides define constants.
*
* TODO(nicksantos): Write a builder to help JSCompiler induce
* {@code replacements} from command-line flags
*
* @param replacements A hash table of names of defines to their replacements.
* All replacements <b>must</b> be literals.
*/
ProcessDefines(AbstractCompiler compiler, Map<String, Node> replacements) {
this.compiler = compiler;
dominantReplacements = replacements;
}
/**
* Injects a pre-computed global namespace, so that the same namespace
* can be re-used for multiple check passes. Returns {@code this} for
* easy chaining.
*/
ProcessDefines injectNamespace(GlobalNamespace namespace) {
this.namespace = namespace;
return this;
}
@Override
public void process(Node externs, Node root) {
if (namespace == null) {
namespace = new GlobalNamespace(compiler, root);
}
overrideDefines(collectDefines(root, namespace));
}
private void overrideDefines(Map<String, DefineInfo> allDefines) {
boolean changed = false;
for (Map.Entry<String, DefineInfo> def : allDefines.entrySet()) {
String defineName = def.getKey();
DefineInfo info = def.getValue();
Node inputValue = dominantReplacements.get(defineName);
Node finalValue = inputValue != null ?
inputValue : info.getLastValue();
if (finalValue != info.initialValue) {
info.initialValueParent.replaceChild(
info.initialValue, finalValue.cloneTree());
compiler.addToDebugLog("Overriding @define variable " + defineName);
changed = changed ||
finalValue.getType() != info.initialValue.getType() ||
!finalValue.isEquivalentTo(info.initialValue);
}
}
if (changed) {
compiler.reportCodeChange();
}
Set<String> unusedReplacements = dominantReplacements.keySet();
unusedReplacements.removeAll(allDefines.keySet());
unusedReplacements.removeAll(KNOWN_DEFINES);
for (String unknownDefine : unusedReplacements) {
compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine));
}
}
private static String format(MessageFormat format, Object... params) {
return format.format(params);
}
/**
* Only defines of literal number, string, or boolean are supported.
*/
private boolean isValidDefineType(JSTypeExpression expression) {
JSType type = expression.evaluate(null, compiler.getTypeRegistry());
return !type.isUnknownType() && type.isSubtype(
compiler.getTypeRegistry().getNativeType(
JSTypeNative.NUMBER_STRING_BOOLEAN));
}
/**
* Finds all defines
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>, and creates a {@link DefineInfo} data structure for
* each one.
* @return A map of {@link DefineInfo} structures, keyed by name.
*/
private Map<String, DefineInfo> collectDefines(Node root,
GlobalNamespace namespace) {
// Find all the global names with a @define annotation
List<Name> allDefines = Lists.newArrayList();
for (Name name : namespace.getNameIndex().values()) {
Ref decl = name.getDeclaration();
if (name.docInfo != null && name.docInfo.isDefine()) {
// Process defines should not depend on check types being enabled,
// so we look for the JSDoc instead of the inferred type.
if (isValidDefineType(name.docInfo.getType())) {
allDefines.add(name);
} else {
JSError error = JSError.make(
decl.getSourceName(),
decl.node, INVALID_DEFINE_TYPE_ERROR);
compiler.report(error);
}
} else {
for (Ref ref : name.getRefs()) {
if (ref == decl) {
// Declarations were handled above.
continue;
}
Node n = ref.node;
Node parent = ref.node.getParent();
JSDocInfo info = n.getJSDocInfo();
if (info == null &&
parent.isVar() && parent.hasOneChild()) {
info = parent.getJSDocInfo();
}
if (info != null && info.isDefine()) {
allDefines.add(name);
break;
}
}
}
}
CollectDefines pass = new CollectDefines(compiler, allDefines);
NodeTraversal.traverse(compiler, root, pass);
return pass.getAllDefines();
}
/**
* Finds all assignments to @defines, and figures out the last value of
* the @define.
*/
private static final class CollectDefines implements Callback {
private final AbstractCompiler compiler;
private final Map<String, DefineInfo> assignableDefines;
private final Map<String, DefineInfo> allDefines;
private final Map<Node, RefInfo> allRefInfo;
// A hack that allows us to remove ASSIGN/VAR statements when
// we're currently visiting one of the children of the assign.
private Node lvalueToRemoveLater = null;
// A stack tied to the node traversal, to keep track of whether
// we're in a conditional block. If 1 is at the top, assignment to
// a define is allowed. Otherwise, it's not allowed.
private final Deque<Integer> assignAllowed;
CollectDefines(AbstractCompiler compiler, List<Name> listOfDefines) {
this.compiler = compiler;
this.allDefines = Maps.newHashMap();
assignableDefines = Maps.newHashMap();
assignAllowed = new ArrayDeque<Integer>();
assignAllowed.push(1);
// Create a map of references to defines keyed by node for easy lookup
allRefInfo = Maps.newHashMap();
for (Name name : listOfDefines) {
Ref decl = name.getDeclaration();
if (decl != null) {
allRefInfo.put(decl.node,
new RefInfo(decl, name));
}
for (Ref ref : name.getRefs()) {
if (ref == decl) {
//
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> Declarations were handled above.
continue;
}
// If there's a TWIN def, only put one of the twins in.
if (ref.getTwin() == null || !ref.getTwin().isSet()) {
allRefInfo.put(ref.node, new RefInfo(ref, name));
}
}
}
}
/**
* Get a map of {@link DefineInfo} structures, keyed by the name of
* the define.
*/
Map<String, DefineInfo> getAllDefines() {
return allDefines;
}
/**
* Keeps track of whether the traversal is in a conditional branch.
* We traverse all nodes of the parse tree.
*/
@Override
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
updateAssignAllowedStack(n, true);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
RefInfo refInfo = allRefInfo.get(n);
if (refInfo != null) {
Ref ref = refInfo.ref;
Name name = refInfo.name;
String fullName = name.getFullName();
switch (ref.type) {
case SET_FROM_GLOBAL:
case SET_FROM_LOCAL:
Node valParent = getValueParent(ref);
Node val = valParent.getLastChild();
if (valParent.isAssign() && name.isSimpleName() &&
name.getDeclaration() == ref) {
// For defines, it's an error if a simple name is assigned
// before it's declared
compiler.report(
t.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName));
} else if (processDefineAssignment(t, fullName, val, valParent)) {
// remove the assignment so that the variable is still declared,
// but no longer assigned to a value, e.g.,
// DEF_FOO = 5; // becomes "5;"
// We can't remove the ASSIGN/VAR when we're still visiting its
// children, so we'll have to come back later to remove it.
refInfo.name.removeRef(ref);
lvalueToRemoveLater = valParent;
}
break;
default:
if (t.inGlobalScope()) {
// Treat this as a reference to a define in the global scope.
// After this point, the define must not be reassigned,
// or it's an error.
DefineInfo info = assignableDefines.get(fullName);
if (info != null) {
setDefineInfoNotAssignable(info, t);
assignableDefines.remove(fullName);
}
}
break;
}
}
if (!t.inGlobalScope() &&
n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) {
// warn about @define annotations in local scopes
compiler.report(
t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, ""));
}
if (lvalueToRemoveLater == n) {
lvalueToRemoveLater = null;
if (n.isAssign()) {
Node last = n.getLastChild();
n.removeChild(last);
parent.replaceChild(n, last);
} else {
Preconditions.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
* traversed, they will be visited by
* {@link #visit(NodeTraversal, Node, Node)} in postorder.</p>
* <p>Implementations can have side effects (e.g. modifying the parse
* tree).</p>
* @return whether the children of this node should be visited
*/
boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent);
/**
* <p>Visits a node in postorder (after its children have been visited).
* A node is visited only if all its parents should be traversed
* ({@link #shouldTraverse(NodeTraversal, Node, Node)}).</p>
* <p>Implementations can have side effects (e.g. modifying the parse
* tree).</p>
*/
void visit(NodeTraversal t, Node n, Node parent);
}
/**
* Callback that also knows about scope changes
*/
public interface ScopedCallback extends Callback {
/**
* Called immediately after entering a new scope. The new scope can
* be accessed through t.getScope()
*/
void enterScope(NodeTraversal t);
/**
* Called immediately before exiting a scope. The ending scope can
* be accessed through t.getScope()
*/
void exitScope(NodeTraversal t);
}
/**
* Abstract callback to visit all nodes in postorder.
*/
public abstract static class AbstractPostOrderCallback implements Callback {
@Override
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
return true;
}
}
/** Abstract callback to visit all nodes in preorder. */
public abstract static class AbstractPreOrderCallback implements Callback {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {}
}
/**
* Abstract scoped callback to visit all nodes in postorder.
*/
public abstract static class AbstractScopedCallback
implements ScopedCallback {
@Override
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
return true;
}
@Override
public void enterScope(NodeTraversal t) {}
@Override
public void exitScope(NodeTraversal t) {}
}
/**
* Abstract callback to visit all nodes but not traverse into function
* bodies.
*/
public abstract static class AbstractShallowCallback implements Callback {
@Override
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
return parent == null || !parent.isFunction() ||
n == parent.getFirstChild();
}
}
/**
* Abstract callback to visit all structure and statement nodes but doesn't
* traverse into functions or expressions.
*/
public abstract static class AbstractShallowStatementCallback
implements Callback {
@Override
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
return parent == null || NodeUtil.isControlStructure(parent)
|| NodeUtil.isStatementBlock(parent);
}
}
/**
* Abstract callback to visit a pruned set of nodes.
*/
public abstract static class AbstractNodeTypePruningCallback
implements Callback {
private final Set<Integer> nodeTypes;
private final boolean
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> include;
/**
* Creates an abstract pruned callback.
* @param nodeTypes the nodes to include in the traversal
*/
public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) {
this(nodeTypes, true);
}
/**
* Creates an abstract pruned callback.
* @param nodeTypes the nodes to include/exclude in the traversal
* @param include whether to include or exclude the nodes in the traversal
*/
public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes,
boolean include) {
this.nodeTypes = nodeTypes;
this.include = include;
}
@Override
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
return include == nodeTypes.contains(n.getType());
}
}
/**
* Creates a node traversal using the specified callback interface.
*/
public NodeTraversal(AbstractCompiler compiler, Callback cb) {
this(compiler, cb, new SyntacticScopeCreator(compiler));
}
/**
* Creates a node traversal using the specified callback interface
* and the scope creator.
*/
public NodeTraversal(AbstractCompiler compiler, Callback cb,
ScopeCreator scopeCreator) {
this.callback = cb;
if (cb instanceof ScopedCallback) {
this.scopeCallback = (ScopedCallback) cb;
}
this.compiler = compiler;
this.inputId = null;
this.sourceName = "";
this.scopeCreator = scopeCreator;
}
private void throwUnexpectedException(Exception unexpectedException) {
// If there's an unexpected exception, try to get the
// line number of the code that caused it.
String message = unexpectedException.getMessage();
// TODO(user): It is possible to get more information if curNode or
// its parent is missing. We still have the scope stack in which it is still
// very useful to find out at least which function caused the exception.
if (inputId != null) {
message =
unexpectedException.getMessage() + "\n" +
formatNodeContext("Node", curNode) +
(curNode == null ?
"" :
formatNodeContext("Parent", curNode.getParent()));
}
compiler.throwInternalError(message, unexpectedException);
}
private String formatNodeContext(String label, Node n) {
if (n == null) {
return " " + label + ": NULL";
}
return " " + label + "(" + n.toString(false, false, false) + "): "
+ formatNodePosition(n);
}
/**
* Traverses a parse tree recursively.
*/
public void traverse(Node root) {
try {
inputId = NodeUtil.getInputId(root);
sourceName = "";
curNode = root;
pushScope(root);
// null parent ensures that the shallow callbacks will traverse root
traverseBranch(root, null);
popScope();
} catch (Exception unexpectedException) {
throwUnexpectedException(unexpectedException);
}
}
public void traverseRoots(Node ... roots) {
traverseRoots(Lists.newArrayList(roots));
}
public void traverseRoots(List<Node> roots) {
if (roots.isEmpty()) {
return;
}
try {
Node scopeRoot = roots.get(0).getParent();
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>);
}
}
public AbstractCompiler getCompiler() {
return compiler;
}
/**
* Gets the current line number, or zero if it cannot be determined. The line
* number is retrieved lazily as a running time optimization.
*/
public int getLineNumber() {
Node cur = curNode;
while (cur != null) {
int line = cur.getLineno();
if (line >= 0) {
return line;
}
cur = cur.getParent();
}
return 0;
}
/**
* Gets the current input source name.
*
* @return A string that may be empty, but not null
*/
public String getSourceName() {
return sourceName;
}
/**
* Gets the current input source.
*/
public CompilerInput getInput() {
return compiler.getInput(inputId);
}
/**
* Gets the current input module.
*/
public JSModule getModule() {
CompilerInput input = getInput();
return input == null ? null : input.getModule();
}
/** Returns the node currently being traversed. */
public Node getCurrentNode() {
return curNode;
}
/**
* Traversal for passes that work only on changed functions.
* Suppose a loopable pass P1 uses this traversal.
* Then, if a function doesn't change between two runs of P1, it won't look at
* the function the second time.
* (We're assuming that P1 runs to a fixpoint, o/w we may miss optimizations.)
*
* Most changes are reported with calls to Compiler.reportCodeChange(), which
* doesn't know which scope changed. We keep track of the current scope by
* calling Compiler.setScope inside pushScope and popScope.
* The automatic tracking can be wrong in rare cases when a pass changes scope
* w/out causing a call to pushScope or popScope. It's very hard to find the
* places where this happens unless a bug is triggered.
* Passes that do cross-scope modifications call
* Compiler.reportChangeToEnclosingScope(Node n).
*/
public static void traverseChangedFunctions(
AbstractCompiler compiler, FunctionCallback callback) {
final AbstractCompiler comp = compiler;
final FunctionCallback cb = callback;
final Node jsRoot = comp.getJsRoot();
NodeTraversal t = new NodeTraversal(comp, new AbstractPreOrderCallback() {
@Override
public final boolean shouldTraverse(NodeTraversal t, Node n, Node p) {
if ((n == jsRoot || n.isFunction()) && comp.hasScopeChanged(n)) {
cb.visit(comp, n);
}
return true;
}
});
t.traverse(jsRoot);
}
/**
* Traverses a node recursively.
*/
public static void traverse(
AbstractCompiler compiler, Node root, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverse(root);
}
/**
* Traverses a list of node trees.
*/
public static void traverseRoots(
AbstractCompiler compiler, List<Node> roots, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverseRoots(roots);
}
public static void traverseRoots(
AbstractCompiler
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> = false;
defineReplacements = Maps.newHashMap();
tweakProcessing = TweakProcessing.OFF;
tweakReplacements = Maps.newHashMap();
moveFunctionDeclarations = false;
appNameStr = "";
recordFunctionInformation = false;
generateExports = false;
exportLocalPropertyDefinitions = false;
cssRenamingMap = null;
cssRenamingWhitelist = null;
processObjectPropertyString = false;
idGenerators = ImmutableMap.of();
replaceStringsFunctionDescriptions = Collections.emptyList();
replaceStringsPlaceholderToken = "";
replaceStringsReservedStrings = Collections.emptySet();
propertyInvalidationErrors = Maps.newHashMap();
// Instrumentation
instrumentationTemplate = null; // instrument functions
instrumentMemoryAllocations = false; // instrument allocations
instrumentForCoverage = false; // instrument lines
// Output
printInputDelimiter = false;
prettyPrint = false;
lineBreak = false;
preferLineBreakAtEndOfFile = false;
reportPath = null;
tracer = TracerMode.OFF;
colorizeErrorOutput = false;
errorFormat = ErrorFormat.SINGLELINE;
debugFunctionSideEffectsPath = null;
externExports = false;
nameReferenceReportPath = null;
nameReferenceGraphPath = null;
// Debugging
aliasHandler = NULL_ALIAS_TRANSFORMATION_HANDLER;
errorHandler = null;
}
/**
* @return Whether to attempt to remove unused class properties
*/
public boolean isRemoveUnusedClassProperties() {
return removeUnusedClassProperties;
}
/**
* @param removeUnusedClassProperties Whether to attempt to remove
* unused class properties
*/
public void setRemoveUnusedClassProperties(boolean removeUnusedClassProperties) {
this.removeUnusedClassProperties = removeUnusedClassProperties;
}
/**
* Returns the map of define replacements.
*/
public Map<String, Node> getDefineReplacements() {
return getReplacementsHelper(defineReplacements);
}
/**
* Returns the map of tweak replacements.
*/
public Map<String, Node> getTweakReplacements() {
return getReplacementsHelper(tweakReplacements);
}
/**
* Creates a map of String->Node from a map of String->Number/String/Boolean.
*/
private static Map<String, Node> getReplacementsHelper(
Map<String, Object> source) {
Map<String, Node> map = Maps.newHashMap();
for (Map.Entry<String, Object> entry : source.entrySet()) {
String name = entry.getKey();
Object value = entry.getValue();
if (value instanceof Boolean) {
map.put(name, NodeUtil.booleanNode(((Boolean) value).booleanValue()));
} else if (value instanceof Integer) {
map.put(name, IR.number(((Integer) value).intValue()));
} else if (value instanceof Double) {
map.put(name, IR.number(((Double) value).doubleValue()));
} else {
Preconditions.checkState(value instanceof String);
map.put(name, IR.string((String) value));
}
}
return map;
}
/**
* Sets the value of the {@code @define} variable in JS
* to a boolean literal.
*/
public void setDefineToBooleanLiteral(String defineName, boolean value) {
define
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>;
}
public LanguageMode getLanguageIn() {
return languageIn;
}
public LanguageMode getLanguageOut() {
return languageOut;
}
/**
* Whether to include "undefined" in the default types.
* For example:
* "{Object}" is normally "Object|null" becomes "Object|null|undefined"
* "{?string}" is normally "string|null" becomes "string|null|undefined"
* In either case "!" annotated types excluded both null and undefined.
*/
public void setLooseTypes(boolean looseTypes) {
this.looseTypes = looseTypes;
}
@Override
public Object clone() throws CloneNotSupportedException {
CompilerOptions clone = (CompilerOptions) super.clone();
// TODO(bolinfest): Add relevant custom cloning.
return clone;
}
public void setAliasTransformationHandler(
AliasTransformationHandler changes) {
this.aliasHandler = changes;
}
public AliasTransformationHandler getAliasTransformationHandler() {
return this.aliasHandler;
}
/**
* Set a custom handler for warnings and errors.
*
* This is mostly used for piping the warnings and errors to
* a file behind the scenes.
*
* If you want to filter warnings and errors, you should use a WarningsGuard.
*
* If you want to change how warnings and errors are reported to the user,
* you should set a ErrorManager on the Compiler. An ErrorManager is
* intended to summarize the errors for a single compile job.
*/
public void setErrorHandler(ErrorHandler handler) {
this.errorHandler = handler;
}
/**
* If true, enables type inference. If checkTypes is enabled, this flag has
* no effect.
*/
public void setInferTypes(boolean enable) {
inferTypes = enable;
}
/**
* Gets the inferTypes flag. Note that if checkTypes is enabled, this flag
* is ignored when configuring the compiler.
*/
public boolean getInferTypes() {
return inferTypes;
}
/**
* @return Whether assumeStrictThis is set.
*/
public boolean assumeStrictThis() {
return assumeStrictThis;
}
/**
* If true, enables enables additional optimizations.
*/
public void setAssumeStrictThis(boolean enable) {
this.assumeStrictThis = enable;
}
/**
* @return Whether assumeClosuresOnlyCaptureReferences is set.
*/
public boolean assumeClosuresOnlyCaptureReferences() {
return assumeClosuresOnlyCaptureReferences;
}
/**
* Whether to assume closures capture only what they reference. This allows
* more aggressive function inlining.
*/
public void setAssumeClosuresOnlyCaptureReferences(boolean enable) {
this.assumeClosuresOnlyCaptureReferences = enable;
}
/**
* Sets the list of properties that we report property invalidation errors
* for.
*/
public void setPropertyInvalidationErrors(
Map<String, CheckLevel> propertyInvalidationErrors) {
this.propertyInvalidationErrors =
Maps.newHashMap(propertyInvalidationErrors);
}
public void setLanguageOut(LanguageMode languageOut) {
this.languageOut = languageOut;
}
public void setIdeMode(boolean ideMode) {
this.ide
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> by only 1 per file, and
* the only top-level structure in the file, this is not enforced.
*/
public interface AliasTransformation {
/**
* Adds an alias definition to the AliasTransformation instance.
* <p>
* Last definition for a given alias is kept if an alias is inserted
* multiple times (since this is generally the behavior in JavaScript code).
*
* @param alias the name of the alias.
* @param definition the definition of the alias.
*/
void addAlias(String alias, String definition);
}
/**
* A Null implementation of the CodeChanges interface which performs all
* operations as a No-Op
*/
static final AliasTransformationHandler NULL_ALIAS_TRANSFORMATION_HANDLER =
new NullAliasTransformationHandler();
private static class NullAliasTransformationHandler
implements AliasTransformationHandler, Serializable {
private static final long serialVersionUID = 0L;
private static final AliasTransformation NULL_ALIAS_TRANSFORMATION =
new NullAliasTransformation();
@Override
public AliasTransformation logAliasTransformation(
String sourceFile, SourcePosition<AliasTransformation> position) {
position.setItem(NULL_ALIAS_TRANSFORMATION);
return NULL_ALIAS_TRANSFORMATION;
}
private static class NullAliasTransformation
implements AliasTransformation, Serializable {
private static final long serialVersionUID = 0L;
@Override
public void addAlias(String alias, String definition) {
}
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* String type.
*/
public final class StringType extends ValueType {
private static final long serialVersionUID = 1L;
StringType(JSTypeRegistry registry) {
super(registry);
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isUnknownType() || that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
}
return FALSE;
}
@Override
public boolean isStringValueType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
// TODO(user): Revisit this for ES4, which is stricter.
return true;
}
@Override
String toStringHelper(boolean forAnnotations) {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "string";
}
@Override
public JSType autoboxes
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>To() {
return getNativeType(JSTypeNative.STRING_OBJECT_TYPE);
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseStringType();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>otes an optional {@code @param}.
*/
public boolean isOptionalArg() {
return root.getType() == Token.EQUALS;
}
/**
* @return Whether this expression denotes a rest args {@code @param}.
*/
public boolean isVarArgs() {
return root.getType() == Token.ELLIPSIS;
}
/**
* Evaluates the type expression into a {@code JSType} object.
*/
public JSType evaluate(StaticScope<JSType> scope, JSTypeRegistry registry) {
JSType type = registry.createFromTypeNodes(root, sourceName, scope);
root.setJSType(type);
return type;
}
@Override
public boolean equals(Object other) {
return other instanceof JSTypeExpression &&
((JSTypeExpression) other).root.isEquivalentTo(root);
}
@Override
public int hashCode() {
return root.toStringTree().hashCode();
}
/**
* @return The source for this type expression. Note that it will not
* contain an expression if there's an @override tag.
*/
public Node getRoot() {
return root;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Nick Santos
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.io.Serializable;
import java.util.Map;
import java.util.Set;
/**
* Representation for a collection of properties on an object.
* @author nicksantos@google.com (Nick Santos)
*/
class PropertyMap implements Serializable {
private static final long serialVersionUID = 1L;
private static final PropertyMap EMPTY_MAP = new PropertyMap(
ImmutableMap.<String, Property>of());
private static final Function<ObjectType, PropertyMap> PROP_MAP_FROM_TYPE =
new Function<ObjectType, PropertyMap>() {
@Override public PropertyMap apply(ObjectType t) {
return t.getPropertyMap();
}
};
// A place to get the inheritance structure.
// Because the extended interfaces are resolved dynamically, this gets
// messy :(. If type-resolution was more well-defined, we could
// just reference primary parents and secondary parents directly.
private ObjectType parentSource = null;
// The map of our own properties.
private final Map<String, Property> properties;
PropertyMap() {
this
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>(Maps.<String, Property>newTreeMap());
}
private PropertyMap(Map<String, Property> underlyingMap) {
this.properties = underlyingMap;
}
static PropertyMap immutableEmptyMap() {
return EMPTY_MAP;
}
void setParentSource(ObjectType ownerType) {
if (this != EMPTY_MAP) {
this.parentSource = ownerType;
}
}
/** Returns the direct parent of this property map. */
PropertyMap getPrimaryParent() {
if (parentSource == null) {
return null;
}
ObjectType iProto = parentSource.getImplicitPrototype();
return iProto == null ? null : iProto.getPropertyMap();
}
/**
* Returns the secondary parents of this property map, for interfaces that
* need multiple inheritance.
*/
Iterable<PropertyMap> getSecondaryParents() {
if (parentSource == null) {
return ImmutableList.of();
}
Iterable<ObjectType> extendedInterfaces =
parentSource.getCtorExtendedInterfaces();
// Most of the time, this will be empty.
if (Iterables.isEmpty(extendedInterfaces)) {
return ImmutableList.of();
}
return Iterables.transform(extendedInterfaces, PROP_MAP_FROM_TYPE);
}
Property getSlot(String name) {
if (properties.containsKey(name)) {
return properties.get(name);
}
PropertyMap primaryParent = getPrimaryParent();
if (primaryParent != null) {
Property prop = primaryParent.getSlot(name);
if (prop != null) {
return prop;
}
}
for (PropertyMap p : getSecondaryParents()) {
if (p != null) {
Property prop = p.getSlot(name);
if (prop != null) {
return prop;
}
}
}
return null;
}
Property getOwnProperty(String propertyName) {
return properties.get(propertyName);
}
int getPropertiesCount() {
PropertyMap primaryParent = getPrimaryParent();
if (primaryParent == null) {
return this.properties.size();
}
Set<String> props = Sets.newHashSet();
collectPropertyNames(props);
return props.size();
}
boolean hasOwnProperty(String propertyName) {
return properties.get(propertyName) != null;
}
boolean hasProperty(String propertyName) {
return getSlot(propertyName) != null;
}
Set<String> getOwnPropertyNames() {
return properties.keySet();
}
void collectPropertyNames(Set<String> props) {
for (String prop : properties.keySet()) {
props.add(prop);
}
PropertyMap primaryParent = getPrimaryParent();
if (primaryParent != null) {
primaryParent.collectPropertyNames(props);
}
for (PropertyMap p : getSecondaryParents()) {
if (p != null) {
p.collectPropertyNames(props);
}
}
}
boolean removeProperty(String name) {
return properties.remove(name) != null;
}
void putProperty(String name, Property newProp) {
Property oldProp = properties.get(name);
if (oldProp != null) {
// This is to keep previously inferred JsDoc info, e.g., in a
// replaceScript scenario.
newProp
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>code> if the edge exists.
*/
public abstract boolean isConnectedInDirection(N n1, E edgeValue, N n2);
@Override
public boolean isConnected(N n1, N n2) {
return isConnectedInDirection(n1, n2) || isConnectedInDirection(n2, n1);
}
@Override
public boolean isConnected(N n1, E e, N n2) {
return isConnectedInDirection(n1, e, n2) ||
isConnectedInDirection(n2, e, n1);
}
/**
* A generic directed graph node.
*
* @param <N> Value type that the graph node stores.
* @param <E> Value type that the graph edge stores.
*/
public static interface DiGraphNode<N, E> extends GraphNode<N, E> {
public List<DiGraphEdge<N, E>> getOutEdges();
public List<DiGraphEdge<N, E>> getInEdges();
}
/**
* A generic directed graph edge.
*
* @param <N> Value type that the graph node stores.
* @param <E> Value type that the graph edge stores.
*/
public static interface DiGraphEdge<N, E> extends GraphEdge<N, E> {
public DiGraphNode<N, E> getSource();
public DiGraphNode<N, E> getDestination();
public void setSource(DiGraphNode<N, E> node);
public void setDestination(DiGraphNode<N, E> node);
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec;
import com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback;
import com.google.javascript.jscomp.type.ReverseAbstractInterpreter;
import com.google.javascript.rhino.Node;
import java.util.Map;
/**
* A compiler pass to run the type inference analysis.
*
*/
class TypeInferencePass implements CompilerPass {
static final DiagnosticType DATAFLOW_ERROR = DiagnosticType.warning(
"JSC_INTERNAL_ERROR_DATAFLOW",
"non-monotonic data-flow analysis");
private final AbstractCompiler compiler;
private final ReverseAbstractInterpreter reverseInterpreter;
private final Scope topScope;
private final MemoizedScopeCreator scopeCreator;
private final Map<String, AssertionFunctionSpec> assertionFunctionsMap;
TypeInferencePass(AbstractCompiler compiler,
ReverseAbstractInterpreter reverseInterpreter,
Scope topScope, MemoizedScopeCreator scopeCreator) {
this.compiler = compiler;
this.reverseInterpreter = reverseInterpreter;
this.topScope = topScope;
this.scopeCreator = scopeCreator;
assertionFunctionsMap = Maps.newHashMap();
for (AssertionFunctionSpec assertionFunction :
compiler.getCodingConvention().getAssertionFunctions()) {
assertionFunctionsMap.put(assertionFunction.getFunctionName(),
assertionFunction);
}
}
/**
* Main entry point for type inference when running over the whole tree.
*
* @param externsRoot The root of the externs parse tree.
* @param jsRoot The root of the input parse tree to be checked.
*/
@Override
public void process(Node externsRoot, Node jsRoot) {
Node externsAndJs = jsRoot.getParent();
Preconditions.checkState(externsAndJs != null);
Preconditions.checkState(
externsRoot == null || externsAndJs.hasChild(externsRoot));
inferAllScopes(externsAndJs);
}
/** Entry point for type inference when running over part of the tree. */
void inferAllScopes(Node node) {
// Type analysis happens in two major phases.
// 1) Finding all the symbols.
// 2) Propagating all the inferred types.
//
// The order of this analysis is non-obvious. In a complete inference
// system, we may need to backtrack arbitrarily far. But the compile-time
// costs would be unaccept
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>able.
//
// We do one pass where we do typed scope creation for all scopes
// in pre-order.
//
// Then we do a second pass where we do all type inference
// (type propagation) in pre-order.
//
// We use a memoized scope creator so that we never create a scope
// more than once.
//
// This will allow us to handle cases like:
// var ns = {};
// (function() { /** JSDoc */ ns.method = function() {}; })();
// ns.method();
// In this code, we need to build the symbol table for the inner scope in
// order to propagate the type of ns.method in the outer scope.
(new NodeTraversal(
compiler, new FirstScopeBuildingCallback(), scopeCreator))
.traverseWithScope(node, topScope);
for (Scope s : scopeCreator.getAllMemoizedScopes()) {
s.resolveTypes();
}
(new NodeTraversal(
compiler, new SecondScopeBuildingCallback(), scopeCreator))
.traverseWithScope(node, topScope);
}
void inferScope(Node n, Scope scope) {
TypeInference typeInference =
new TypeInference(
compiler, computeCfg(n), reverseInterpreter, scope,
assertionFunctionsMap);
try {
typeInference.analyze();
// Resolve any new type names found during the inference.
compiler.getTypeRegistry().resolveTypesInScope(scope);
} catch (DataFlowAnalysis.MaxIterationsExceededException e) {
compiler.report(JSError.make(n.getSourceFileName(), n, DATAFLOW_ERROR));
}
}
private class FirstScopeBuildingCallback extends AbstractScopedCallback {
@Override
public void enterScope(NodeTraversal t) {
t.getScope();
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
// Do nothing
}
}
private class SecondScopeBuildingCallback extends AbstractScopedCallback {
@Override
public void enterScope(NodeTraversal t) {
// Only infer the entry root, rather than the scope root.
// This ensures that incremental compilation only touches the root
// that's been swapped out.
inferScope(t.getCurrentNode(), t.getScope());
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
// Do nothing
}
}
private ControlFlowGraph<Node> computeCfg(Node n) {
ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false);
cfa.process(null, n);
return cfa.getCfg();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.io.Serializable;
import java.util.*;
import java.util.Map;
import java.util.TreeSet;
/**
* WarningsGuard that represents just a chain of other guards. For example we
* could have following chain
* 1) all warnings outside of /foo/ should be suppressed
* 2) errors with key JSC_BAR should be marked as warning
* 3) the rest should be reported as error
*
* This class is designed for such behavior.
*
* @author anatol@google.com (Anatol Pomazau)
*/
public class ComposeWarningsGuard extends WarningsGuard {
private static final long serialVersionUID = 1L;
// The order that the guards were added in.
private final Map<WarningsGuard, Integer> orderOfAddition = Maps.newHashMap();
private int numberOfAdds = 0;
private final Comparator<WarningsGuard> guardComparator =
new GuardComparator(orderOfAddition);
private boolean demoteErrors = false;
private static class GuardComparator
implements Comparator<WarningsGuard>, Serializable {
private static final long serialVersionUID = 1L;
private final Map<WarningsGuard, Integer> orderOfAddition;
private GuardComparator(Map<WarningsGuard, Integer> orderOfAddition) {
this.orderOfAddition = orderOfAddition;
}
@Override
public int compare(WarningsGuard a, WarningsGuard b) {
int priorityDiff = a.getPriority() - b.getPriority();
if (priorityDiff != 0) {
return priorityDiff;
}
// If the warnings guards have the same priority, the one that
// was added last wins.
return orderOfAddition.get(b).intValue() -
orderOfAddition.get(a).intValue();
}
}
// The order that the guards are applied in.
private final TreeSet<WarningsGuard> guards =
new TreeSet<WarningsGuard>(guardComparator);
public ComposeWarningsGuard(List<WarningsGuard> guards) {
addGuards(guards);
}
public ComposeWarningsGuard(WarningsGuard... guards) {
this(Lists.newArrayList(guards));
}
void addGuard(WarningsGuard guard) {
if (guard instanceof ComposeWarningsGuard) {
ComposeWarningsGuard composeGuard = (ComposeWarningsGuard) guard;
if (composeGuard.demoteErrors) {
this
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.demoteErrors = composeGuard.demoteErrors;
}
// Reverse the guards, so that they have the same order in the result.
addGuards(Lists.newArrayList(composeGuard.guards.descendingSet()));
} else {
numberOfAdds++;
orderOfAddition.put(guard, numberOfAdds);
guards.remove(guard);
guards.add(guard);
}
}
private void addGuards(Iterable<WarningsGuard> guards) {
for (WarningsGuard guard : guards) {
addGuard(guard);
}
}
@Override
public CheckLevel level(JSError error) {
for (WarningsGuard guard : guards) {
CheckLevel newLevel = guard.level(error);
if (newLevel != null) {
if (demoteErrors && newLevel == CheckLevel.ERROR) {
return CheckLevel.WARNING;
}
return newLevel;
}
}
return null;
}
@Override
public boolean disables(DiagnosticGroup group) {
nextSingleton:
for (DiagnosticType type : group.getTypes()) {
DiagnosticGroup singleton = DiagnosticGroup.forType(type);
for (WarningsGuard guard : guards) {
if (guard.disables(singleton)) {
continue nextSingleton;
} else if (guard.enables(singleton)) {
return false;
}
}
return false;
}
return true;
}
/**
* Determines whether this guard will "elevate" the status of any disabled
* diagnostic type in the group to a warning or an error.
*/
@Override
public boolean enables(DiagnosticGroup group) {
for (WarningsGuard guard : guards) {
if (guard.enables(group)) {
return true;
} else if (guard.disables(group)) {
return false;
}
}
return false;
}
List<WarningsGuard> getGuards() {
return Collections.unmodifiableList(Lists.newArrayList(guards));
}
/**
* Make a warnings guard that's the same as this one but with
* all escalating guards turned down.
*/
ComposeWarningsGuard makeEmergencyFailSafeGuard() {
ComposeWarningsGuard safeGuard = new ComposeWarningsGuard();
safeGuard.demoteErrors = true;
for (WarningsGuard guard : guards.descendingSet()) {
safeGuard.addGuard(guard);
}
return safeGuard;
}
@Override
public String toString() {
return Joiner.on(", ").join(guards);
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.SortedSet;
/**
* <p>A basic error manager that sorts all errors and warnings reported to it to
* generate a sorted report when the {@link #generateReport()} method
* is called.</p>
*
* <p>This error manager does not produce any output, but subclasses can
* override the {@link #println(CheckLevel, JSError)} method to generate custom
* output.</p>
*
*/
public abstract class BasicErrorManager implements ErrorManager {
private final SortedSet<ErrorWithLevel> messages =
Sets.newTreeSet(new LeveledJSErrorComparator());
private int errorCount = 0;
private int warningCount = 0;
private double typedPercent = 0.0;
@Override
public void report(CheckLevel level, JSError error) {
if (messages.add(new ErrorWithLevel(error, level))) {
if (level == CheckLevel.ERROR) {
errorCount++;
} else if (level == CheckLevel.WARNING) {
warningCount++;
}
}
}
@Override
public void generateReport() {
for (ErrorWithLevel message : messages) {
println(message.level, message.error);
}
printSummary();
}
/**
* Print a message with a trailing new line. This method is called by the
* {@link #generateReport()} method when generating messages.
*/
public abstract void println(CheckLevel level, JSError error);
/**
* Print the summary of the compilation - number of errors and warnings.
*/
protected abstract void printSummary();
@Override
public int getErrorCount() {
return errorCount;
}
@Override
public int getWarningCount() {
return warningCount;
}
@Override
public JSError[] getErrors() {
return toArray(CheckLevel.ERROR);
}
@Override
public JSError[] getWarnings() {
return toArray(CheckLevel.WARNING);
}
@Override
public void setTypedPercent(double typedPercent) {
this.typedPercent = typedPercent;
}
@Override
public double getTypedPercent() {
return typedPercent;
}
private JSError[] toArray(CheckLevel level) {
List<JSError> errors = new ArrayList<JSError>(messages.size());
for (ErrorWithLevel p : messages) {
if (p.level == level) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
errors.add(p.error);
}
}
return errors.toArray(new JSError[errors.size()]);
}
/**
* <p>Comparator of {@link JSError} with an associated {@link CheckLevel}.
* The ordering is the standard lexical ordering on the quintuple
* (file name, line number, {@link CheckLevel},
* character number, description).</p>
*
* <p>Note: this comparator imposes orderings that are inconsistent with
* {@link JSError#equals(Object)}.</p>
*/
static final class LeveledJSErrorComparator
implements Comparator<ErrorWithLevel> {
private static final int P1_LT_P2 = -1;
private static final int P1_GT_P2 = 1;
@Override
public int compare(ErrorWithLevel p1, ErrorWithLevel p2) {
// null is the smallest value
if (p2 == null) {
if (p1 == null) {
return 0;
} else {
return P1_GT_P2;
}
}
// check level
if (p1.level != p2.level) {
return p2.level.compareTo(p1.level);
}
// sourceName comparison
String source1 = p1.error.sourceName;
String source2 = p2.error.sourceName;
if (source1 != null && source2 != null) {
int sourceCompare = source1.compareTo(source2);
if (sourceCompare != 0) {
return sourceCompare;
}
} else if (source1 == null && source2 != null) {
return P1_LT_P2;
} else if (source1 != null && source2 == null) {
return P1_GT_P2;
}
// lineno comparison
int lineno1 = p1.error.lineNumber;
int lineno2 = p2.error.lineNumber;
if (lineno1 != lineno2) {
return lineno1 - lineno2;
} else if (lineno1 < 0 && 0 <= lineno2) {
return P1_LT_P2;
} else if (0 <= lineno1 && lineno2 < 0) {
return P1_GT_P2;
}
// charno comparison
int charno1 = p1.error.getCharno();
int charno2 = p2.error.getCharno();
if (charno1 != charno2) {
return charno1 - charno2;
} else if (charno1 < 0 && 0 <= charno2) {
return P1_LT_P2;
} else if (0 <= charno1 && charno2 < 0) {
return P1_GT_P2;
}
// description
return p1.error.description.compareTo(p2.error.description);
}
}
static class ErrorWithLevel {
final JSError error;
final CheckLevel level;
ErrorWithLevel(JSError error, CheckLevel level) {
this.error = error;
this.level = level;
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2013 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
/**
* Records whether the code has changed since the last reset.
* @author nicksantos@google.com (Nick Santos)
*/
final class RecentChange implements CodeChangeHandler {
private boolean hasChanged = false;
@Override
public void reportChange() {
hasChanged = true;
}
public boolean hasCodeChanged() {
return hasChanged;
}
public void reset() {
hasChanged = false;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>FunctionScope() {
return cache.functionScope;
}
/** Whether this flows from a bottom scope. */
private boolean flowsFromBottom() {
return getFunctionScope().isBottom();
}
/**
* Creates an entry lattice for the flow.
*/
public static LinkedFlowScope createEntryLattice(Scope scope) {
return new LinkedFlowScope(new FlatFlowScopeCache(scope));
}
@Override
public void inferSlotType(String symbol, JSType type) {
Preconditions.checkState(!frozen);
lastSlot = new LinkedFlowSlot(symbol, type, lastSlot);
depth++;
cache.dirtySymbols.add(symbol);
}
@Override
public void inferQualifiedSlot(Node node, String symbol, JSType bottomType,
JSType inferredType) {
Scope functionScope = getFunctionScope();
if (functionScope.isLocal()) {
if (functionScope.getVar(symbol) == null && !functionScope.isBottom()) {
functionScope.declare(symbol, node, bottomType, null);
}
inferSlotType(symbol, inferredType);
}
}
@Override
public JSType getTypeOfThis() {
return cache.functionScope.getTypeOfThis();
}
@Override
public Node getRootNode() {
return getFunctionScope().getRootNode();
}
@Override
public StaticScope<JSType> getParentScope() {
return getFunctionScope().getParentScope();
}
/**
* Get the slot for the given symbol.
*/
@Override
public StaticSlot<JSType> getSlot(String name) {
if (cache.dirtySymbols.contains(name)) {
for (LinkedFlowSlot slot = lastSlot;
slot != null; slot = slot.parent) {
if (slot.getName().equals(name)) {
return slot;
}
}
}
return cache.getSlot(name);
}
@Override
public StaticSlot<JSType> getOwnSlot(String name) {
throw new UnsupportedOperationException();
}
@Override
public FlowScope createChildFlowScope() {
frozen = true;
if (depth > MAX_DEPTH) {
if (flattened == null) {
flattened = new FlatFlowScopeCache(this);
}
return new LinkedFlowScope(flattened);
}
return new LinkedFlowScope(this);
}
/**
* Iterate through all the linked flow scopes before this one.
* If there's one and only one slot defined between this scope
* and the blind scope, return it.
*/
@Override
public StaticSlot<JSType> findUniqueRefinedSlot(FlowScope blindScope) {
StaticSlot<JSType> result = null;
for (LinkedFlowScope currentScope = this;
currentScope != blindScope;
currentScope = currentScope.parent) {
for (LinkedFlowSlot currentSlot = currentScope.lastSlot;
currentSlot != null &&
(currentScope.parent == null ||
currentScope.parent.lastSlot != currentSlot);
currentSlot = currentSlot.parent) {
if (result == null) {
result = currentSlot;
} else if (!currentSlot.getName().equals(result.getName())) {
return null;
}
}
}
return result;
}
/**
* Look through the
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> given scope, and try to find slots where it doesn't
* have enough type information. Then fill in that type information
* with stuff that we've inferred in the local flow.
*/
@Override
public void completeScope(StaticScope<JSType> staticScope) {
Scope scope = (Scope) staticScope;
for (Iterator<Var> it = scope.getVars(); it.hasNext();) {
Var var = it.next();
if (var.isTypeInferred()) {
JSType type = var.getType();
if (type == null || type.isUnknownType()) {
JSType flowType = getSlot(var.getName()).getType();
var.setType(flowType);
}
}
}
}
/**
* Remove flow scopes that add nothing to the flow.
*/
// NOTE(nicksantos): This function breaks findUniqueRefinedSlot, because
// findUniqueRefinedSlot assumes that this scope is a direct descendant
// of blindScope. This is not necessarily true if this scope has been
// optimize()d and blindScope has not. This should be fixed. For now,
// we only use optimize() where we know that we won't have to do
// a findUniqueRefinedSlot on it.
@Override
public LinkedFlowScope optimize() {
LinkedFlowScope current;
for (current = this;
current.parent != null &&
current.lastSlot == current.parent.lastSlot;
current = current.parent) {}
return current;
}
/** Join the two FlowScopes. */
static class FlowScopeJoinOp extends JoinOp.BinaryJoinOp<FlowScope> {
@SuppressWarnings("unchecked")
@Override
public FlowScope apply(FlowScope a, FlowScope b) {
// To join the two scopes, we have to
LinkedFlowScope linkedA = (LinkedFlowScope) a;
LinkedFlowScope linkedB = (LinkedFlowScope) b;
linkedA.frozen = true;
linkedB.frozen = true;
if (linkedA.optimize() == linkedB.optimize()) {
return linkedA.createChildFlowScope();
}
return new LinkedFlowScope(new FlatFlowScopeCache(linkedA, linkedB));
}
}
@Override
public boolean equals(Object other) {
if (other instanceof LinkedFlowScope) {
LinkedFlowScope that = (LinkedFlowScope) other;
if (this.optimize() == that.optimize()) {
return true;
}
// If two flow scopes are in the same function, then they could have
// two possible function scopes: the real one and the BOTTOM scope.
// If they have different function scopes, we *should* iterate through all
// the variables in each scope and compare. However, 99.9% of the time,
// they're not equal. And the other .1% of the time, we can pretend
// they're equal--this just means that data flow analysis will have
// to propagate the entry lattice a little bit further than it
// really needs to. Everything will still come out ok.
if (this.getFunctionScope() != that.getFunctionScope()) {
return false;
}
if (cache == that.cache) {
// If the two flow scopes have the same cache, then we can check
// equality
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> a lot faster: by just looking at the "dirty" elements
// in the cache, and comparing them in both scopes.
for (String name : cache.dirtySymbols) {
if (diffSlots(getSlot(name), that.getSlot(name))) {
return false;
}
}
return true;
}
Map<String, StaticSlot<JSType>> myFlowSlots = allFlowSlots();
Map<String, StaticSlot<JSType>> otherFlowSlots = that.allFlowSlots();
for (StaticSlot<JSType> slot : myFlowSlots.values()) {
if (diffSlots(slot, otherFlowSlots.get(slot.getName()))) {
return false;
}
otherFlowSlots.remove(slot.getName());
}
for (StaticSlot<JSType> slot : otherFlowSlots.values()) {
if (diffSlots(slot, myFlowSlots.get(slot.getName()))) {
return false;
}
}
return true;
}
return false;
}
/**
* Determines whether two slots are meaningfully different for the
* purposes of data flow analysis.
*/
private boolean diffSlots(StaticSlot<JSType> slotA,
StaticSlot<JSType> slotB) {
boolean aIsNull = slotA == null || slotA.getType() == null;
boolean bIsNull = slotB == null || slotB.getType() == null;
if (aIsNull && bIsNull) {
return false;
} else if (aIsNull ^ bIsNull) {
return true;
}
// Both slots and types must be non-null.
return slotA.getType().differsFrom(slotB.getType());
}
/**
* Gets all the symbols that have been defined before this point
* in the current flow. Does not return slots that have not changed during
* the flow.
*
* For example, consider the code:
* <code>
* var x = 3;
* function f() {
* var y = 5;
* y = 6; // FLOW POINT
* var z = y;
* return z;
* }
* </code>
* A FlowScope at FLOW POINT will return a slot for y, but not
* a slot for x or z.
*/
private Map<String, StaticSlot<JSType>> allFlowSlots() {
Map<String, StaticSlot<JSType>> slots = Maps.newHashMap();
for (LinkedFlowSlot slot = lastSlot;
slot != null; slot = slot.parent) {
if (!slots.containsKey(slot.getName())) {
slots.put(slot.getName(), slot);
}
}
for (Map.Entry<String, StaticSlot<JSType>> symbolEntry : cache.symbols.entrySet()) {
if (!slots.containsKey(symbolEntry.getKey())) {
slots.put(symbolEntry.getKey(), symbolEntry.getValue());
}
}
return slots;
}
@Override
public int hashCode() {
throw new UnsupportedOperationException();
}
/**
* A static slot that can be used in a linked list.
*/
private static class LinkedFlowSlot extends SimpleSlot {
final LinkedFlowSlot parent;
LinkedFlowSlot(String name, JSType type, LinkedFlowSlot parent) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
super(name, type, true);
this.parent = parent;
}
}
/**
* A map that tries to cache as much symbol table information
* as possible in a map. Optimized for fast lookup.
*/
private static class FlatFlowScopeCache {
// The Scope for the entire function or for the global scope.
private final Scope functionScope;
// The linked flow scope that this cache represents.
private final LinkedFlowScope linkedEquivalent;
// All the symbols defined before this point in the local flow.
// May not include lazily declared qualified names.
private Map<String, StaticSlot<JSType>> symbols = Maps.newHashMap();
// Used to help make lookup faster for LinkedFlowScopes by recording
// symbols that may be redefined "soon", for an arbitrary definition
// of "soon". ;)
//
// More rigorously, if a symbol is redefined in a LinkedFlowScope,
// and this is the closest FlatFlowScopeCache, then that symbol is marked
// "dirty". In this way, we don't waste time looking in the LinkedFlowScope
// list for symbols that aren't defined anywhere nearby.
final Set<String> dirtySymbols = Sets.newHashSet();
// The cache at the bottom of the lattice.
FlatFlowScopeCache(Scope functionScope) {
this.functionScope = functionScope;
symbols = ImmutableMap.of();
linkedEquivalent = null;
}
// A cache in the middle of a long scope chain.
FlatFlowScopeCache(LinkedFlowScope directParent) {
FlatFlowScopeCache cache = directParent.cache;
functionScope = cache.functionScope;
symbols = directParent.allFlowSlots();
linkedEquivalent = directParent;
}
// A cache at the join of two scope chains.
FlatFlowScopeCache(LinkedFlowScope joinedScopeA,
LinkedFlowScope joinedScopeB) {
linkedEquivalent = null;
// Always prefer the "real" function scope to the faked-out
// bottom scope.
functionScope = joinedScopeA.flowsFromBottom() ?
joinedScopeB.getFunctionScope() : joinedScopeA.getFunctionScope();
Map<String, StaticSlot<JSType>> slotsA = joinedScopeA.allFlowSlots();
Map<String, StaticSlot<JSType>> slotsB = joinedScopeB.allFlowSlots();
symbols = slotsA;
// There are 5 different join cases:
// 1) The type is declared in joinedScopeA, not in joinedScopeB,
// and not in functionScope. Just use the one in A.
// 2) The type is declared in joinedScopeB, not in joinedScopeA,
// and not in functionScope. Just use the one in B.
// 3) The type is declared in functionScope and joinedScopeA, but
// not in joinedScopeB. Join the two types.
// 4) The type is declared in functionScope and joinedScopeB, but
// not in joinedScopeA. Join the two types.
// 5) The type is declared in joinedScopeA and joinedScopeB. Join
// the two types.
Set<String> symbolNames = Sets.newHashSet(symbols.keySet());
symbolNames.addAll(slotsB.keySet());
for (String name : symbolNames
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
/**
* The {@code Unknown} type.
*/
public class UnknownType extends ObjectType {
private static final long serialVersionUID = 1L;
// See the explanation of checked unknown types in JSTypeNative.
private final boolean isChecked;
UnknownType(JSTypeRegistry registry, boolean isChecked) {
super(registry);
this.isChecked = isChecked;
}
@Override
public boolean isUnknownType() {
return true;
}
@Override
public boolean isCheckedUnknownType() {
return isChecked;
}
@Override
public boolean canBeCalled() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public TernaryValue testForEquality(JSType that) {
return UNKNOWN;
}
@Override
public boolean isNullable() {
return true;
}
@Override
public boolean isSubtype(JSType that) {
return true;
}
@Override
public <T> T visit(Visitor<T> visitor)
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> {
return visitor.caseUnknownType();
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseUnknownType(this, that);
}
@Override
String toStringHelper(boolean forAnnotations) {
return getReferenceName();
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, Node propertyNode) {
// nothing to define
return true;
}
@Override
public ObjectType getImplicitPrototype() {
return null;
}
@Override
public FunctionType getConstructor() {
return null;
}
@Override
public String getReferenceName() {
return isChecked ? "??" : "?";
}
@Override
public String getDisplayName() {
return "Unknown";
}
@Override
public boolean hasDisplayName() {
return true;
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
return this;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import com.google.debugging.sourcemap.FilePosition;
import com.google.debugging.sourcemap.SourceMapFormat;
import com.google.debugging.sourcemap.SourceMapGenerator;
import com.google.debugging.sourcemap.SourceMapGeneratorFactory;
import com.google.debugging.sourcemap.SourceMapGeneratorV1;
import com.google.debugging.sourcemap.SourceMapGeneratorV2;
import com.google.javascript.rhino.Node;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Collects information mapping the generated (compiled) source back to
* its original source for debugging purposes.
*
* @see CodeConsumer
* @see CodeGenerator
* @see CodePrinter
*
*/
public class SourceMap {
public static enum Format {
V1 {
@Override SourceMap getInstance() {
return new SourceMap(
SourceMapGeneratorFactory.getInstance(SourceMapFormat.V1));
}
},
DEFAULT {
@Override SourceMap getInstance() {
return new SourceMap(
SourceMapGeneratorFactory.getInstance(SourceMapFormat.DEFAULT));
}
},
V2 {
@Override SourceMap getInstance() {
return new SourceMap(
SourceMapGeneratorFactory.getInstance(SourceMapFormat.V2));
}
},
V3 {
@Override SourceMap getInstance() {
return new SourceMap(
SourceMapGeneratorFactory.getInstance(SourceMapFormat.V3));
}
};
abstract SourceMap getInstance();
}
/**
* Source maps can be very large different levels of detail can be specified.
*/
public static enum DetailLevel implements Predicate<Node> {
// ALL is best when the fullest details are needed for debugging or for
// code-origin analysis.
ALL {
@Override public boolean apply(Node node) {
return true;
}
},
// SYMBOLS is intended to be used for stack trace deobfuscation when full
// detail is not needed.
SYMBOLS {
@Override public boolean apply(Node node) {
return node.isCall()
|| node.isNew()
|| node.isFunction()
|| node.isName()
|| NodeUtil.isGet(node)
|| NodeUtil.isObjectLitKey(node)
|| (node.isString() && NodeUtil.isGet(node.getParent()));
}
};
}
public
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> static class LocationMapping {
final String prefix;
final String replacement;
public LocationMapping(String prefix, String replacement) {
this.prefix = prefix;
this.replacement = replacement;
}
}
private final SourceMapGenerator generator;
private List<LocationMapping> prefixMappings = Collections.emptyList();
private final Map<String, String> sourceLocationFixupCache =
Maps.newHashMap();
private SourceMap(SourceMapGenerator generator) {
this.generator = generator;
}
public void addMapping(
Node node,
FilePosition outputStartPosition,
FilePosition outputEndPosition) {
String sourceFile = node.getSourceFileName();
// If the node does not have an associated source file or
// its line number is -1, then the node does not have sufficient
// information for a mapping to be useful.
if (sourceFile == null || node.getLineno() < 0) {
return;
}
sourceFile = fixupSourceLocation(sourceFile);
String originalName = (String) node.getProp(Node.ORIGINALNAME_PROP);
// Strangely, Rhino source lines are one based but columns are
// zero based.
// We don't change this for the v1 or v2 source maps but for
// v3 we make them both 0 based.
int lineBaseOffset = 1;
if (generator instanceof SourceMapGeneratorV1
|| generator instanceof SourceMapGeneratorV2) {
lineBaseOffset = 0;
}
generator.addMapping(
sourceFile, originalName,
new FilePosition(node.getLineno() - lineBaseOffset, node.getCharno()),
outputStartPosition, outputEndPosition);
}
/**
* @param sourceFile The source file location to fixup.
* @return a remapped source file.
*/
private String fixupSourceLocation(String sourceFile) {
if (prefixMappings.isEmpty()) {
return sourceFile;
}
String fixed = sourceLocationFixupCache.get(sourceFile);
if (fixed != null) {
return fixed;
}
// Replace the first prefix found with its replacement
for (LocationMapping mapping : prefixMappings) {
if (sourceFile.startsWith(mapping.prefix)) {
fixed = mapping.replacement + sourceFile.substring(
mapping.prefix.length());
break;
}
}
// If none of the mappings match then use the original file path.
if (fixed == null) {
fixed = sourceFile;
}
sourceLocationFixupCache.put(sourceFile, fixed);
return fixed;
}
public void appendTo(Appendable out, String name) throws IOException {
generator.appendTo(out, name);
}
public void reset() {
generator.reset();
sourceLocationFixupCache.clear();
}
public void setStartingPosition(int offsetLine, int offsetIndex) {
generator.setStartingPosition(offsetLine, offsetIndex);
}
public void setWrapperPrefix(String prefix) {
generator.setWrapperPrefix(prefix);
}
public void validate(boolean validate) {
generator.validate(validate);
}
/**
* @param sourceMapLocationMappings
*/
public void setPrefixMappings(List<LocationMapping> sourceMapLocationMappings) {
this.prefix
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.javascript.jscomp.deps.SortedDependencies;
import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException;
import com.google.javascript.jscomp.deps.SortedDependencies.MissingProvideException;
import com.google.javascript.jscomp.graph.LinkedDirectedGraph;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* A {@link JSModule} dependency graph that assigns a depth to each module and
* can answer depth-related queries about them. For the purposes of this class,
* a module's depth is defined as the number of hops in the longest path from
* the module to a module with no dependencies.
*
*/
public class JSModuleGraph {
private List<JSModule> modules;
/**
* Lists of modules at each depth. <code>modulesByDepth.get(3)</code> is a
* list of the modules at depth 3, for example.
*/
private List<List<JSModule>> modulesByDepth;
/**
* dependencyMap is a cache of dependencies that makes the dependsOn
* function faster. Each map entry associates a starting
* JSModule with the set of JSModules that are transitively dependent on the
* starting module.
*
* If the cache returns null, then the entry hasn't been filled in for that
* module.
*
* dependencyMap should be filled from leaf to root so that
* getTransitiveDepsDeepestFirst can use its results directly.
*/
private Map<JSModule, Set<JSModule>> dependencyMap = Maps.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>newHashMap();
/**
* Creates a module graph from a list of modules in dependency order.
*/
public JSModuleGraph(JSModule[] modulesInDepOrder) {
this(ImmutableList.copyOf(modulesInDepOrder));
}
/**
* Creates a module graph from a list of modules in dependency order.
*/
public JSModuleGraph(List<JSModule> modulesInDepOrder) {
Preconditions.checkState(
modulesInDepOrder.size() == Sets.newHashSet(modulesInDepOrder).size(),
"Found duplicate modules");
modules = ImmutableList.copyOf(modulesInDepOrder);
modulesByDepth = Lists.newArrayList();
for (JSModule module : modulesInDepOrder) {
int depth = 0;
for (JSModule dep : module.getDependencies()) {
int depDepth = dep.getDepth();
if (depDepth < 0) {
throw new ModuleDependenceException(String.format(
"Modules not in dependency order: %s preceded %s",
module.getName(), dep.getName()),
module, dep);
}
depth = Math.max(depth, depDepth + 1);
}
module.setDepth(depth);
if (depth == modulesByDepth.size()) {
modulesByDepth.add(new ArrayList<JSModule>());
}
modulesByDepth.get(depth).add(module);
}
}
/**
* Gets an iterable over all modules in dependency order.
*/
Iterable<JSModule> getAllModules() {
return modules;
}
/**
* Gets all modules indexed by name.
*/
Map<String, JSModule> getModulesByName() {
Map<String, JSModule> result = Maps.newHashMap();
for (JSModule m : modules) {
result.put(m.getName(), m);
}
return result;
}
/**
* Gets the total number of modules.
*/
int getModuleCount() {
return modules.size();
}
/**
* Gets the root module.
*/
JSModule getRootModule() {
return Iterables.getOnlyElement(modulesByDepth.get(0));
}
/**
* Returns a JSON representation of the JSModuleGraph. Specifically a
* JSONArray of "Modules" where each module has a
* - "name"
* - "dependencies" (list of module names)
* - "transitive-dependencies" (list of module names, deepest first)
* - "inputs" (list of file names)
* @return List of module JSONObjects.
*/
JSONArray toJson() {
JSONArray modules = new JSONArray();
for (JSModule module : getAllModules()) {
JSONObject node = new JSONObject();
try {
node.put("name", module.getName());
JSONArray deps = new JSONArray();
node.put("dependencies", deps);
for (JSModule m : module.getDependencies()) {
deps.put(m.getName());
}
JSONArray transitiveDeps = new JSONArray();
node.put("transitive-dependencies", transitiveDeps);
for (JSModule m : getTransitiveDepsDeepestFirst(module)) {
transitiveDeps.put(m.getName());
}
JSONArray inputs = new JSONArray();
node.put("inputs", inputs);
for (CompilerInput input : module
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.getInputs()) {
inputs.put(input.getSourceFile().getOriginalPath());
}
modules.put(node);
} catch (JSONException e) {
Throwables.propagate(e);
}
}
return modules;
}
/**
* Determines whether this module depends on a given module. Note that a
* module never depends on itself, as that dependency would be cyclic.
*/
public boolean dependsOn(JSModule src, JSModule m) {
Set<JSModule> deps = dependencyMap.get(src);
if (deps == null) {
deps = getTransitiveDepsDeepestFirst(src);
dependencyMap.put(src, deps);
}
return deps.contains(m);
}
/**
* Finds the deepest common dependency of two modules, not including the two
* modules themselves.
*
* @param m1 A module in this graph
* @param m2 A module in this graph
* @return The deepest common dep of {@code m1} and {@code m2}, or null if
* they have no common dependencies
*/
JSModule getDeepestCommonDependency(JSModule m1, JSModule m2) {
int m1Depth = m1.getDepth();
int m2Depth = m2.getDepth();
// According our definition of depth, the result must have a strictly
// smaller depth than either m1 or m2.
for (int depth = Math.min(m1Depth, m2Depth) - 1; depth >= 0; depth--) {
List<JSModule> modulesAtDepth = modulesByDepth.get(depth);
// Look at the modules at this depth in reverse order, so that we use the
// original ordering of the modules to break ties (later meaning deeper).
for (int i = modulesAtDepth.size() - 1; i >= 0; i--) {
JSModule m = modulesAtDepth.get(i);
if (dependsOn(m1, m) && dependsOn(m2, m)) {
return m;
}
}
}
return null;
}
/**
* Finds the deepest common dependency of two modules, including the
* modules themselves.
*
* @param m1 A module in this graph
* @param m2 A module in this graph
* @return The deepest common dep of {@code m1} and {@code m2}, or null if
* they have no common dependencies
*/
public JSModule getDeepestCommonDependencyInclusive(
JSModule m1, JSModule m2) {
if (m2 == m1 || dependsOn(m2, m1)) {
return m1;
} else if (dependsOn(m1, m2)) {
return m2;
}
return getDeepestCommonDependency(m1, m2);
}
/** Returns the deepest common dependency of the given modules. */
public JSModule getDeepestCommonDependencyInclusive(
Collection<JSModule> modules) {
Iterator<JSModule> iter = modules.iterator();
JSModule dep = iter.next();
while (iter.hasNext()) {
dep = getDeepestCommonDependencyInclusive(dep, iter.next());
}
return dep
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>;
}
/**
* Creates an iterable over the transitive dependencies of module {@code m}
* in a non-increasing depth ordering. The result does not include the module
* {@code m}.
*
* @param m A module in this graph
* @return The transitive dependencies of module {@code m}
*/
Set<JSModule> getTransitiveDepsDeepestFirst(JSModule m) {
Set<JSModule> deps = dependencyMap.get(m);
if (deps != null) {
return deps;
}
deps = new TreeSet<JSModule>(new InverseDepthComparator());
addDeps(deps, m);
dependencyMap.put(m, deps);
return deps;
}
/**
* Adds a module's transitive dependencies to a set.
*/
private void addDeps(Set<JSModule> deps, JSModule m) {
for (JSModule dep : m.getDependencies()) {
deps.add(dep);
addDeps(deps, dep);
}
}
/**
* Replaces any files that are found multiple times with a single instance in
* the closest parent module that is common to all modules where it appears.
*
* JSCompiler normally errors if you attempt to compile modules containing the
* same file. This method can be used to remove duplicates before compiling
* to avoid such an error.
*/
public void coalesceDuplicateFiles() {
Multimap<String, JSModule> fileRefs = LinkedHashMultimap.create();
for (JSModule module : modules) {
for (CompilerInput jsFile : module.getInputs()) {
fileRefs.put(jsFile.getName(), module);
}
}
for (String path : fileRefs.keySet()) {
Collection<JSModule> refModules = fileRefs.get(path);
if (refModules.size() > 1) {
JSModule depModule = getDeepestCommonDependencyInclusive(refModules);
CompilerInput file = refModules.iterator().next().getByName(path);
for (JSModule module : refModules) {
if (module != depModule) {
module.removeByName(path);
}
}
if (!refModules.contains(depModule)) {
depModule.add(file);
}
}
}
}
/**
* Applies a DependencyOptions in "dependency sorting" and "dependency pruning"
* mode to the given list of inputs. Returns a new list with the files sorted
* and removed. This module graph will be updated to reflect the new list.
*
* If you need more fine-grained dependency management, you should create your
* own DependencyOptions and call
* {@code manageDependencies(DependencyOptions, List<CompilerInput>)}.
*
* @param entryPoints The entry points into the program.
* Expressed as JS symbols.
* @param inputs The original list of sources. Used to ensure that the sort
* is stable.
* @throws CircularDependencyException if there is a circular dependency
* between the provides and requires.
* @throws MissingProvideException if an entry point was not provided
* by any of the inputs.
* @see DependencyOptions for more info on how this works.
*/
public List<CompilerInput> manageDependencies(
List<String> entry
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> createEntryPointInputs(
DependencyOptions depOptions,
List<CompilerInput> inputs,
SortedDependencies<CompilerInput> sorter)
throws MissingModuleException, MissingProvideException {
Set<CompilerInput> entryPointInputs = Sets.newLinkedHashSet();
Map<String, JSModule> modulesByName = getModulesByName();
if (depOptions.shouldPruneDependencies()) {
if (!depOptions.shouldDropMoochers()) {
entryPointInputs.addAll(sorter.getInputsWithoutProvides());
}
for (String entryPoint : depOptions.getEntryPoints()) {
// An entry point is either formatted as:
// 'foo.bar' - peg foo.bar to its current module
// 'modC:foo.bar' - peg foo.bar to modC
String inputName = entryPoint;
int splitPoint = entryPoint.indexOf(':');
CompilerInput entryPointInput = null;
if (splitPoint != -1) {
String moduleName = entryPoint.substring(0, splitPoint);
inputName = entryPoint.substring(
Math.min(splitPoint + 1, entryPoint.length() - 1));
JSModule module = modulesByName.get(moduleName);
if (module == null) {
throw new MissingModuleException(moduleName);
} else {
entryPointInput = sorter.getInputProviding(inputName);
entryPointInput.overrideModule(module);
}
} else {
entryPointInput = sorter.getInputProviding(inputName);
}
entryPointInputs.add(entryPointInput);
}
CompilerInput baseJs = sorter.maybeGetInputProviding("goog");
if (baseJs != null) {
entryPointInputs.add(baseJs);
}
} else {
entryPointInputs.addAll(inputs);
}
return entryPointInputs;
}
LinkedDirectedGraph<JSModule, String> toGraphvizGraph() {
LinkedDirectedGraph<JSModule, String> graphViz =
LinkedDirectedGraph.create();
for (JSModule module : getAllModules()) {
graphViz.createNode(module);
for (JSModule dep : module.getDependencies()) {
graphViz.createNode(dep);
graphViz.connect(module, "->", dep);
}
}
return graphViz;
}
/**
* A module depth comparator that considers a deeper module to be "less than"
* a shallower module. Uses module names to consistently break ties.
*/
private class InverseDepthComparator implements Comparator<JSModule> {
@Override
public int compare(JSModule m1, JSModule m2) {
return depthCompare(m2, m1);
}
}
private int depthCompare(JSModule m1, JSModule m2) {
if (m1 == m2) {
return 0;
}
int d1 = m1.getDepth();
int d2 = m2.getDepth();
return d1 < d2 ? -1 : d2 == d1 ? m1.getName().compareTo(m2.getName()) : 1;
}
/**
* Exception class for declaring when the modules being fed into a
* JSModuleGraph as input aren't in dependence order, and so can't be
* processed for caching of various dependency-
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> in {1}");
static final DiagnosticType VAR_ARGUMENTS_SHADOWED_ERROR =
DiagnosticType.error(
"JSC_VAR_ARGUMENTS_SHADOWED_ERROR",
"Shadowing \"arguments\" is not allowed");
// The arguments variable is special, in that it's declared in every local
// scope, but not explicitly declared.
private static final String ARGUMENTS = "arguments";
// Vars that still need to be declared in externs. These will be declared
// at the end of the pass, or when we see the equivalent var declared
// in the normal code.
private final Set<String> varsToDeclareInExterns = Sets.newHashSet();
private final AbstractCompiler compiler;
// Whether this is the post-processing sanity check.
private final boolean sanityCheck;
// Whether extern checks emit error.
private final boolean strictExternCheck;
VarCheck(AbstractCompiler compiler) {
this(compiler, false);
}
VarCheck(AbstractCompiler compiler, boolean sanityCheck) {
this.compiler = compiler;
this.strictExternCheck = compiler.getErrorLevel(
JSError.make("", 0, 0, UNDEFINED_EXTERN_VAR_ERROR)) == CheckLevel.ERROR;
this.sanityCheck = sanityCheck;
}
/**
* Create a SyntacticScopeCreator. If not in sanity check mode, use a
* {@link RedeclarationCheckHandler} to check var redeclarations.
* @return the SyntacticScopeCreator
*/
private ScopeCreator createScopeCreator() {
if (sanityCheck) {
return new SyntacticScopeCreator(compiler);
} else {
return new SyntacticScopeCreator(
compiler, new RedeclarationCheckHandler());
}
}
@Override
public void process(Node externs, Node root) {
ScopeCreator scopeCreator = createScopeCreator();
// Don't run externs-checking in sanity check mode. Normalization will
// remove duplicate VAR declarations, which will make
// externs look like they have assigns.
if (!sanityCheck) {
NodeTraversal traversal = new NodeTraversal(
compiler, new NameRefInExternsCheck(), scopeCreator);
traversal.traverse(externs);
}
NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator);
t.traverseRoots(Lists.newArrayList(externs, root));
for (String varName : varsToDeclareInExterns) {
createSynthesizedExternVar(varName);
}
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
Preconditions.checkState(scriptRoot.isScript());
ScopeCreator scopeCreator = createScopeCreator();
NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator);
// Note we use the global scope to prevent wrong "undefined-var errors" on
// variables that are defined in other JS files.
Scope topScope = scopeCreator.createScope(compiler.getRoot(), null);
t.traverseWithScope(scriptRoot, topScope);
// TODO(bashir) Check if we need to createSynthesizedExternVar like process.
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (!n.isName()) {
return;
}
String varName = n.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>getString();
// Only a function can have an empty name.
if (varName.isEmpty()) {
Preconditions.checkState(parent.isFunction());
Preconditions.checkState(NodeUtil.isFunctionExpression(parent));
return;
}
// Check if this is a declaration for a var that has been declared
// elsewhere. If so, mark it as a duplicate.
if ((parent.isVar() ||
NodeUtil.isFunctionDeclaration(parent)) &&
varsToDeclareInExterns.contains(varName)) {
createSynthesizedExternVar(varName);
n.addSuppression("duplicate");
}
// Check that the var has been declared.
Scope scope = t.getScope();
Scope.Var var = scope.getVar(varName);
if (var == null) {
if (NodeUtil.isFunctionExpression(parent)) {
// e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the
// current scope.
} else {
boolean isArguments = scope.isLocal() && ARGUMENTS.equals(varName);
// The extern checks are stricter, don't report a second error.
if (!isArguments && !(strictExternCheck && t.getInput().isExtern())) {
t.report(n, UNDEFINED_VAR_ERROR, varName);
}
if (sanityCheck) {
throw new IllegalStateException("Unexpected variable " + varName);
} else {
createSynthesizedExternVar(varName);
scope.getGlobalScope().declare(varName, n,
null, compiler.getSynthesizedExternsInput());
}
}
return;
}
CompilerInput currInput = t.getInput();
CompilerInput varInput = var.input;
if (currInput == varInput || currInput == null || varInput == null) {
// The variable was defined in the same file. This is fine.
return;
}
// Check module dependencies.
JSModule currModule = currInput.getModule();
JSModule varModule = varInput.getModule();
JSModuleGraph moduleGraph = compiler.getModuleGraph();
if (!sanityCheck &&
varModule != currModule && varModule != null && currModule != null) {
if (moduleGraph.dependsOn(currModule, varModule)) {
// The module dependency was properly declared.
} else {
if (scope.isGlobal()) {
if (moduleGraph.dependsOn(varModule, currModule)) {
// The variable reference violates a declared module dependency.
t.report(n, VIOLATED_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
} else {
// The variable reference is between two modules that have no
// dependency relationship. This should probably be considered an
// error, but just issue a warning for now.
t.report(n, MISSING_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
}
} else {
t.report(n, STRICT_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
}
}
}
}
/**
* Create a new variable in a synthetic script. This will prevent
* subsequent compiler passes from crashing.
*/
private void createSynthesizedExtern
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Var(String varName) {
Node nameNode = IR.name(varName);
// Mark the variable as constant if it matches the coding convention
// for constant vars.
// NOTE(nicksantos): honestly, I'm not sure how much this matters.
// AFAIK, all people who use the CONST coding convention also
// compile with undeclaredVars as errors. We have some test
// cases for this configuration though, and it makes them happier.
if (compiler.getCodingConvention().isConstant(varName)) {
nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
getSynthesizedExternsRoot().addChildToBack(
IR.var(nameNode));
varsToDeclareInExterns.remove(varName);
compiler.reportCodeChange();
}
/**
* A check for name references in the externs inputs. These used to prevent
* a variable from getting renamed, but no longer have any effect.
*/
private class NameRefInExternsCheck extends AbstractPostOrderCallback {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
switch (parent.getType()) {
case Token.VAR:
case Token.FUNCTION:
case Token.PARAM_LIST:
// These are okay.
break;
case Token.GETPROP:
if (n == parent.getFirstChild()) {
Scope scope = t.getScope();
Scope.Var var = scope.getVar(n.getString());
if (var == null) {
t.report(n, UNDEFINED_EXTERN_VAR_ERROR, n.getString());
varsToDeclareInExterns.add(n.getString());
}
}
break;
default:
t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString());
Scope scope = t.getScope();
Scope.Var var = scope.getVar(n.getString());
if (var == null) {
varsToDeclareInExterns.add(n.getString());
}
break;
}
}
}
}
/**
* @param n The name node to check.
* @param origVar The associated Var.
* @return Whether duplicated declarations warnings should be suppressed
* for the given node.
*/
static boolean hasDuplicateDeclarationSuppression(Node n, Scope.Var origVar) {
Preconditions.checkState(n.isName());
Node parent = n.getParent();
Node origParent = origVar.getParentNode();
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
info = parent.getJSDocInfo();
}
if (info != null && info.getSuppressions().contains("duplicate")) {
return true;
}
info = origVar.nameNode.getJSDocInfo();
if (info == null) {
info = origParent.getJSDocInfo();
}
return (info != null && info.getSuppressions().contains("duplicate"));
}
/**
* The handler for duplicate declarations.
*/
private class RedeclarationCheckHandler implements RedeclarationHandler {
@Override
public void onRedeclaration(
Scope s, String name, Node n,
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> checkingPolicy;
/*
* Eventize DAG represented using adjacency lists.
*/
private Map<String, Set<String>> eventizes;
/*
* Maps from eventful object name to state.
*/
private static Map<String, EventfulObjectState> eventfulObjectMap;
public CheckEventfulObjectDisposal(AbstractCompiler compiler,
DisposalCheckingPolicy checkingPolicy) {
this.compiler = compiler;
this.checkingPolicy = checkingPolicy;
this.initializeDisposeMethodsMap();
this.typeRegistry = compiler.getTypeRegistry();
}
/**
* Add a new call that is used to dispose an JS object.
* @param functionOrMethodName The name or suffix of a function or method
* that disposes of/registers an object as disposable
* @param argumentsThatAreDisposed An array of integers (ideally sorted) that
* specifies the arguments of the function being disposed
*/
private void addDisposeCall(String functionOrMethodName,
List<Integer> argumentsThatAreDisposed) {
String potentiallyTypeName, propertyName;
JSType objectType = null;
int lastPeriod = functionOrMethodName.lastIndexOf('.');
// If function call has a period it is potentially a method function.
if (lastPeriod >= 0) {
potentiallyTypeName = functionOrMethodName.substring(0, lastPeriod).
replaceFirst(".prototype$", "");
propertyName = functionOrMethodName.substring(lastPeriod);
objectType = compiler.getTypeRegistry().getType(potentiallyTypeName);
} else {
propertyName = functionOrMethodName;
}
// Find or create property map for object type
Map<String, List<Integer>> map = this.disposeCalls.get(objectType);
if (map == null) {
map = Maps.newHashMap();
this.disposeCalls.put(objectType, map);
}
/*
* If this is a static function call store the full function name,
* else only the method of the object.
*/
if (objectType == null) {
map.put(functionOrMethodName, argumentsThatAreDisposed);
} else {
map.put(propertyName, argumentsThatAreDisposed);
}
}
/*
* Initialize disposeMethods map with calls to dispose calls.
*/
private void initializeDisposeMethodsMap() {
this.disposeCalls = Maps.newHashMap();
/*
* Initialize dispose calls map. Checks for:
* - Y.registerDisposable(X)
* (Y has to be of type goog.Disposable)
* - X.dispose()
* - goog.dispose(X)
* - goog.disposeAll(X...)
* - X.removeAll() (X is of type goog.events.EventHandler)
* - Y.add(X...) or Y.push(X)
*/
this.addDisposeCall("goog.dispose",
new ArrayList<Integer>(Arrays.asList(0)));
this.addDisposeCall("goog.Disposable.registerDisposable",
new ArrayList<Integer>(Arrays.asList(0)));
this.addDisposeCall("goog.disposeAll",
new ArrayList<Integer>(Arrays.asList(DISPOSE_ALL)));
this.addDisposeCall("goog.events.EventHandler.removeAll",
new ArrayList<Integer>(Arrays.asList(DISPOSE_SELF)));
this.addDisposeCall(".dispose",
new ArrayList
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS><Integer>(Arrays.asList(DISPOSE_SELF)));
this.addDisposeCall(".push",
new ArrayList<Integer>(Arrays.asList(0)));
this.addDisposeCall(".add",
new ArrayList<Integer>(Arrays.asList(DISPOSE_ALL)));
}
private static Node getBase(Node n) {
Node base = n;
while (base.isGetProp()) {
base = base.getFirstChild();
}
return base;
}
/*
* Get the type of the this in the current scope of traversal
*/
private JSType getTypeOfThisForScope(NodeTraversal t) {
JSType typeOfThis = t.getScopeRoot().getJSType();
if (typeOfThis == null) {
return null;
}
ObjectType objectType =
ObjectType.cast(dereference(typeOfThis));
return objectType.getTypeOfThis();
}
/**
* Determines if thisType is possibly a subtype of thatType.
*
* It differs from isSubtype only in that thisType gets expanded
* if it is a union.
*
* Common case targeted is a function returning an eventful object
* that may also return a null.
*
* @param thisType the JSType being tested
* @param thatType the JSType that is possibly a base of thisType
* @return whether thisType is possibly subtype of thatType
*/
private static boolean isPossiblySubtype(JSType thisType, JSType thatType) {
if (thisType == null) {
return false;
}
JSType type = thisType;
if (type.isUnionType()) {
for (JSType alternate : type.toMaybeUnionType().getAlternates()) {
if (alternate.isSubtype(thatType)) {
return true;
}
}
} else {
if (type.isSubtype(thatType)) {
return true;
}
}
return false;
}
private static JSType dereference(JSType type) {
return type == null ? null : type.dereference();
}
/*
* Create a unique identification string for Node n, or null if function
* called with invalid argument.
*
* This function is basically used to distinguish between:
* A.B = function() {
* this.eh = new ...
* }
* and
* C.D = function() {
* this.eh = new ...
* }
*
* As well as
* A.B = function() {
* var eh = new ...
* }
* and
* C.D = function() {
* var eh = new ...
* }
*
* Warning: Inheritance is not currently handled.
*/
private static String generateKey(NodeTraversal t, Node n,
boolean noLocalVariables) {
if (n == null) {
return null;
}
String key;
Node scopeNode = t.getScopeRoot();
if (n.isName()) {
if (noLocalVariables) {
return null;
}
key = n.getQualifiedName();
if (scopeNode.isFunction()) {
JSType parentScopeType = t.getScope().getParentScope().getTypeOfThis();
/*
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
* If the locally defined variable is defined within a function, use
* the function name to create ID.
*/
if (!parentScopeType.isGlobalThisType()) {
key = parentScopeType.toString() + "~" + key;
}
key = NodeUtil.getFunctionName(scopeNode) + "=" + key;
}
} else {
/*
* Only handle cases such as a.b.c.X and not cases where the
* eventful object is stored in an array or uses a function to
* determine the index.
*
* Note: Inheritance changes the name that should be returned here
*/
if (!n.isQualifiedName()) {
return null;
}
key = n.getQualifiedName();
/*
* If it is not a simple variable and doesn't use this, then we assume
* global variable.
*/
Node base = getBase(n);
if (base != null && base.isThis()) {
if (base.getJSType().isUnknownType()) {
// Handle anonymous function created in constructor:
//
// /**
// * @extends {goog.SubDisposable}
// * @constructor */
// speel.Person = function() {
// this.run = function() {
// this.eh = new goog.events.EventHandler();
// }
//};
key = t.getScope().getParentScope().getTypeOfThis().toString() + "~"
+ key;
} else {
if (n.getFirstChild() == null) {
key = base.getJSType().toString() + "=" + key;
} else {
ObjectType objectType =
ObjectType.cast(dereference(n.getFirstChild().getJSType()));
if (objectType == null) {
return null;
}
ObjectType hObjT = objectType;
String propertyName = n.getLastChild().getString();
while (objectType != null) {
hObjT = objectType;
objectType = objectType.getImplicitPrototype();
if (objectType == null) {
break;
}
if (objectType.getDisplayName().endsWith("prototype")) {
continue;
}
if (!objectType.getPropertyNames().contains(propertyName)) {
break;
}
}
key = hObjT.toString() + "=" + key;
}
}
}
}
return key;
}
@Override
public void process(Node externs, Node root) {
// This pass should not have gotten added in this case
Preconditions.checkArgument(checkingPolicy != DisposalCheckingPolicy.OFF);
// Initialize types
googDisposableInterfaceType =
compiler.getTypeRegistry().getType(DISPOSABLE_INTERFACE_TYPE_NAME);
googEventsEventHandlerType = compiler.getTypeRegistry()
.getType(EVENT_HANDLER_TYPE_NAME);
/*
* Required types not found therefore the kind of pattern considered
* will not be found.
*/
if (googEventsEventHandlerType == null ||
googDisposableInterfaceType == null) {
return;
}
// Seed list of disposable stype
eventfulTypes = new HashSet<JSType>();
eventfulTypes.add(googEventsEventHandlerType);
// Construct eventizer graph
if (checkingPolicy == DisposalCheckingPolicy.AGGRESSIVE) {
NodeTraversal.traverse(compiler, root
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>, new ComputeEventizeTraversal());
computeEventful();
}
/*
* eventfulObjectMap maps a eventful object's "name" to its corresponding
* EventfulObjectState which tracks the state (allocated, disposed of)
* as well as allocation site.
*/
eventfulObjectMap = new HashMap<String, EventfulObjectState>();
// Traverse tree
NodeTraversal.traverse(compiler, root, new Traversal());
/*
* Scan eventfulObjectMap for allocated eventful objects that
* had no dispose calls.
*/
for (EventfulObjectState e : eventfulObjectMap.values()) {
Node n = e.allocationSite;
if (e.seen == SeenType.ALLOCATED) {
compiler.report(JSError.make(n.getSourceFileName(), n,
EVENTFUL_OBJECT_NOT_DISPOSED));
} else if (e.seen == SeenType.ALLOCATED_LOCALLY &&
checkingPolicy == DisposalCheckingPolicy.AGGRESSIVE) {
compiler.report(JSError.make(n.getSourceFileName(), n,
EVENTFUL_OBJECT_PURELY_LOCAL));
}
}
}
private void computeEventful() {
/*
* Topological order of Eventize DAG
*/
String[] order = new String[eventizes.size()];
/*
* Perform topological sort
*/
int white = 0, gray = 1, black = 2;
int last = eventizes.size() - 1;
Map<String, Integer> color = new HashMap<String, Integer>();
Stack<String> dfsStack = new Stack<String>();
/*
* Initialize color.
* Some types are only on one or the other side of the
* inference.
*/
for (String r : eventizes.keySet()) {
color.put(r, white);
for (String s : eventizes.get(r)) {
color.put(s, white);
}
}
int indx = 0;
for (String s : eventizes.keySet()) {
dfsStack.push(s);
while (dfsStack.size() > 0) {
String top = dfsStack.pop();
if (!color.containsKey(top)) {
continue;
}
if (color.get(top) == white) {
color.put(top, gray);
dfsStack.push(top);
// for v in Adj[s]
if (eventizes.containsKey(top)) {
for (String v : eventizes.get(top)) {
if (color.get(v) == white) {
dfsStack.push(v);
}
}
}
} else if (color.get(top) == gray && eventizes.containsKey(top)) {
order[last - indx] = top;
++indx;
color.put(top, black);
}
}
}
/*
* Propagate eventfulness by iterating in topological order
*/
for (String s : order) {
if (eventfulTypes.contains(typeRegistry.getType(s))) {
for (String v : eventizes.get(s)) {
eventfulTypes.add(typeRegistry.getType(v));
}
}
}
}
private JS
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> (JSType type : ut.getAlternates()) {
if (type.isObject()) {
addEventizeClass(className, type);
}
}
} else {
addEventizeClass(className, thatType);
}
}
private void addEventizeClass(String className, JSType thatType) {
String propertyJsTypeName = thatType.getDisplayName();
Set<String> eventize = eventizes.get(propertyJsTypeName);
if (eventize == null) {
eventize = new HashSet<String>();
eventizes.put(propertyJsTypeName, eventize);
}
eventize.add(className);
}
@Override
public void enterScope(NodeTraversal t) {
Node n = t.getScopeRoot();
boolean isConstructor = false;
boolean isInDisposal = false;
String functionName = null;
/*
* Scope entered is a function definition
*/
if (n.isFunction()) {
functionName = NodeUtil.getFunctionName(n);
/*
* Skip anonymous functions
*/
if (functionName != null) {
JSDocInfo jsDocInfo = NodeUtil.getBestJSDocInfo(n);
if (jsDocInfo != null) {
/*
* Record constructor of a type
*/
if (jsDocInfo.isConstructor()) {
isConstructor = true;
/*
* Initialize eventizes relationship
*/
if (t.getScope() != null &&
t.getScope().getTypeOfThis() != null) {
ObjectType objectType = ObjectType.cast(t.getScope()
.getTypeOfThis().dereference());
/*
* Eventize due to inheritance
*/
while (objectType != null) {
objectType = objectType.getImplicitPrototype();
if (objectType == null) {
break;
}
if (objectType.getDisplayName().endsWith("prototype")) {
continue;
}
addEventize(compiler.getTypeRegistry().getType(functionName),
objectType);
/*
* Don't add transitive eventize edges here, it will be
* taken care of in computeEventful
*/
break;
}
}
}
}
/*
* Indicate within a disposeInternal member
*/
if (functionName.endsWith(".disposeInternal")) {
isInDisposal = true;
}
}
isConstructorStack.push(isConstructor);
isDisposalStack.push(isInDisposal);
} else {
isConstructorStack.push(inConstructorScope());
isDisposalStack.push(inDisposalScope());
}
}
@Override
public void exitScope(NodeTraversal t) {
isConstructorStack.pop();
isDisposalStack.pop();
}
/*
* Is the current node a call to goog.events.unlisten
*/
private void isGoogEventsUnlisten(Node n) {
Preconditions.checkArgument(n.getChildCount() > 3);
Node listener = n.getChildAtIndex(3);
Node objectWithListener = n.getChildAtIndex(1);
if (!objectWithListener.isQualifiedName()) {
return;
}
if (listener.isFunction()) {
/*
* Anonymous function
*/
compiler.report(JSError.make(n.getSourceFileName(), n,
UNLISTEN_
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>WITH_ANONBOUND));
} else if (listener.isCall()) {
if (!listener.getFirstChild().isQualifiedName()) {
/*
* Anonymous function
*/
compiler.report(JSError.make(n.getSourceFileName(), n,
UNLISTEN_WITH_ANONBOUND));
} else if (listener.getFirstChild().getQualifiedName()
.equals("goog.bind")) {
/*
* Using goog.bind to unlisten
*/
compiler.report(JSError.make(n.getSourceFileName(), n,
UNLISTEN_WITH_ANONBOUND));
}
}
}
private void visitCall(NodeTraversal t, Node n) {
Node functionCalled = n.getFirstChild();
if (functionCalled == null ||
!functionCalled.isQualifiedName()) {
return;
}
String functionCalledName = functionCalled.getQualifiedName();
JSType typeOfThis = getTypeOfThisForScope(t);
if (typeOfThis == null) {
return;
}
/*
* Class considered eventful if there is an unlisten call in the
* disposal.
*/
if (functionCalledName.equals("goog.events.unlisten")) {
if (inDisposalScope()) {
eventfulTypes.add(typeOfThis);
}
isGoogEventsUnlisten(n);
}
if (inDisposalScope() &&
functionCalledName.equals("goog.events.removeAll")) {
eventfulTypes.add(typeOfThis);
}
/*
* If member with qualified name gets disposed of when this class
* gets disposed, consider the member type as an eventizer of this
* class.
*/
JSType disposedType = maybeReturnDisposedType(n, inDisposalScope());
if (!collectorFilterType(disposedType)) {
addEventize(getTypeOfThisForScope(t), disposedType);
}
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.CALL:
visitCall(t, n);
break;
default:
break;
}
}
}
private class Traversal extends AbstractPostOrderCallback
implements ScopedCallback {
/*
* Checks if the input node correspond to the creation of an eventful object
*/
private boolean createsEventfulObject(Node n) {
Node first = n.getFirstChild();
JSType type = n.getJSType();
if (first == null ||
!first.isQualifiedName() ||
type.isEmptyType() ||
type.isUnknownType()) {
return false;
}
boolean isOfTypeNeedingDisposal = false;
for (JSType disposableType : eventfulTypes) {
if (type.isSubtype(disposableType)) {
isOfTypeNeedingDisposal = true;
break;
}
}
return isOfTypeNeedingDisposal;
}
/*
* This function traverses the current scope to see if a locally
* defined eventful object is assigned to a live-out variable.
*
* Note: This function could be called multiple times to traverse
* the same scope if multiple local eventful objects are created in the
* scope.
*/
private Node localEventfulObjectAssign(
NodeTraversal t, Node property
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Node) {
Node parent;
if (!t.getScope().isGlobal()) {
/*
* In function
*/
parent = NodeUtil.getFunctionBody(t.getScopeRoot());
} else {
/*
* In global scope
*/
parent = t.getScopeRoot().getFirstChild();
}
/*
* Check to see if locally created EventHandler is assigned to field
*/
for (Node sibling : parent.children()) {
if (sibling.isExprResult()) {
Node assign = sibling.getFirstChild();
if (assign.isAssign()) {
// assign.getLastChild().isEquivalentTo(propertyNode) did not work
if (propertyNode.getQualifiedName().equals(assign.getLastChild()
.getQualifiedName())) {
if (!assign.getFirstChild().isName()) {
return assign.getFirstChild();
}
}
}
}
}
/*
* Eventful object created and assigned to a local variable which is not
* assigned to another variable in a way to allow disposal.
*/
String key = generateKey(t, propertyNode, false);
if (key == null) {
return null;
}
EventfulObjectState e;
if (eventfulObjectMap.containsKey(key)) {
e = eventfulObjectMap.get(key);
if (e.seen == SeenType.ALLOCATED) {
e.seen = SeenType.ALLOCATED_LOCALLY;
}
} else {
e = new EventfulObjectState();
e.seen = SeenType.ALLOCATED_LOCALLY;
eventfulObjectMap.put(key, e);
}
e.allocationSite = propertyNode;
return null;
}
/*
* Record the creation of a new eventful object.
*/
private void visitNew(NodeTraversal t, Node n, Node parent) {
if (!createsEventfulObject(n)) {
return;
}
/*
* Insert allocation site and construct into eventfulObjectMap
*/
String key;
Node propertyNode;
/*
* Handles (E is an eventful class):
* - object.something = new E();
* - local = new E();
* - var local = new E();
*/
if (parent.isAssign()) {
propertyNode = parent.getFirstChild();
} else {
propertyNode = parent;
}
key = generateKey(t, propertyNode, false);
if (key == null) {
return;
}
EventfulObjectState e;
if (eventfulObjectMap.containsKey(key)) {
e = eventfulObjectMap.get(key);
} else {
e = new EventfulObjectState();
e.seen = SeenType.ALLOCATED;
eventfulObjectMap.put(key, e);
}
e.allocationSite = propertyNode;
/*
* Check if locally defined eventful object is assigned to global variable
* and create an entry mapping to the previous site.
*/
if (propertyNode.isName()) {
Node globalVarNode = localEventfulObjectAssign(t, propertyNode);
if (globalVarNode != null) {
key = generateKey(t, globalVarNode, false);
if (key == null) {
/*
* Local variable
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> is assigned to an array or in a manner requiring
* a function call.
*/
e.seen = SeenType.POSSIBLY_DISPOSED;
return;
}
eventfulObjectMap.put(key, e);
}
}
}
private void addDisposeArgumentsMatched(Map<String, List<Integer>> map,
Node n, String property, List<Node> foundDisposeCalls) {
for (String disposeMethod : map.keySet()) {
if (property.endsWith(disposeMethod)) {
List<Integer> disposeArguments = map.get(disposeMethod);
// Dispose specific arguments only
Node t = n.getNext();
int tsArgument = 0;
for (Integer disposeArgument : disposeArguments) {
switch (disposeArgument) {
// Dispose all arguments
case DISPOSE_ALL:
for (Node tt = n.getNext(); tt != null; tt = tt.getNext()) {
foundDisposeCalls.add(tt);
}
break;
// Dispose objects called on
case DISPOSE_SELF:
Node calledOn = n.getFirstChild();
foundDisposeCalls.add(calledOn);
break;
default:
// The current item pointed to by t is beyond that requested in
// current array element.
if (tsArgument > disposeArgument) {
t = n.getNext();
tsArgument = 0;
}
for (; tsArgument < disposeArgument && t != null;
++tsArgument) {
t = t.getNext();
}
if (tsArgument == disposeArgument && t != null) {
foundDisposeCalls.add(t);
}
break;
}
}
}
}
}
private List<Node> maybeGetValueNodesFromCall(Node n) {
List<Node> ret = Lists.newArrayList();
Node first = n.getFirstChild();
if (first == null || !first.isQualifiedName()) {
return ret;
}
String property = first.getQualifiedName();
Node base = first.getFirstChild();
JSType baseType = null;
if (base != null) {
baseType = base.getJSType();
}
for (JSType key : disposeCalls.keySet()) {
if (key == null ||
(baseType != null && isPossiblySubtype(baseType, key))) {
addDisposeArgumentsMatched(disposeCalls.get(key), first, property,
ret);
}
}
return ret;
}
/*
* Look for calls to an eventful object's disposal functions.
* (dispose or removeAll will remove all event listeners from
* an EventHandler).
*/
private void visitCall(NodeTraversal t, Node n) {
// Filter the calls to find a "dispose" call
List<Node> variableNodes = maybeGetValueNodesFromCall(n);
for (Node variableNode : variableNodes) {
Preconditions.checkState(variableNode != null);
// Only consider removals on eventful object
boolean isTrackedRemoval = false;
JSType vnType = variableNode.getJSType();
for (JSType type : eventfulTypes) {
if (isPossiblySubtype(vnType, type)) {
isTrackedRemoval = true;
}
}
if (!isTrackedRemoval) {
continue;
}
String key = generate
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Key(t, variableNode, false);
if (key == null) {
continue;
}
eventfulObjectDisposed(t, variableNode);
}
}
/**
* Dereference a type, autoboxing it and filtering out null.
* From {@link CheckAccessControls}
*/
private JSType dereference(JSType type) {
return type == null ? null : type.dereference();
}
/*
* Check function definitions to add custom dispose methods.
*/
public void visitFunction(NodeTraversal t, Node n) {
Preconditions.checkArgument(n.isFunction());
JSDocInfo jsDocInfo = NodeUtil.getFunctionJSDocInfo(n);
// Function annotated to dispose of
if (jsDocInfo != null && jsDocInfo.isDisposes()) {
JSType type = n.getJSType();
if (type == null || type.isUnknownType()) {
return;
}
FunctionType funType = type.toMaybeFunctionType();
Node paramNode = NodeUtil.getFunctionParameters(n).getFirstChild();
List<Integer> positionalDisposedParameters = Lists.newArrayList();
if (jsDocInfo.disposesOf("*")) {
positionalDisposedParameters.add(DISPOSE_ALL);
} else {
// Parameter types
int index = 0;
for (Node p : funType.getParameters()) {
// Bail out if the paramNode is not there.
if (paramNode == null) {
break;
}
if (jsDocInfo.disposesOf(paramNode.getString())) {
positionalDisposedParameters.add(index);
}
paramNode = paramNode.getNext();
index++;
}
}
addDisposeCall(NodeUtil.getFunctionName(n),
positionalDisposedParameters);
}
}
/*
* Track assignments to see if a private field is being
* overwritten.
*
* Assigning to an array element is taken care of by the generateKey
* returning null on array ("complex") variable names.
*/
public void visitAssign(NodeTraversal t, Node n) {
Node assignedTo = n.getFirstChild();
JSType assignedToType = assignedTo.getJSType();
if (assignedToType == null || assignedToType.isEmptyType()) {
return;
}
if (n.getFirstChild().isGetProp()) {
boolean isTrackedAssign = false;
for (JSType disposalType : eventfulTypes) {
if (assignedToType.isSubtype(disposalType)) {
isTrackedAssign = true;
break;
}
}
if (!isTrackedAssign) {
return;
}
JSDocInfo di = n.getJSDocInfo();
ObjectType objectType =
ObjectType.cast(dereference(n.getFirstChild().getFirstChild()
.getJSType()));
String propertyName = n.getFirstChild().getLastChild().getString();
boolean fieldIsPrivate = (
(di != null) &&
(di.getVisibility() == Visibility.PRIVATE));
/*
* See if field is defined as private in superclass
*/
while (objectType != null) {
di = null;
objectType = objectType.getImplicitPrototype();
if (objectType == null) {
break;
}
/*
* Skip prototype definitions:
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> * Don't flag a field declared private in assignment as well
* as in prototype declaration
* Assumption: The inheritance hierarchy is similar to
* class
* class.prototype
* superclass
* superclass.prototype
*/
if (objectType.getDisplayName().endsWith("prototype")) {
continue;
}
di = objectType.getOwnPropertyJSDocInfo(propertyName);
if (di != null) {
if (fieldIsPrivate || di.getVisibility() == Visibility.PRIVATE) {
compiler.report(
t.makeError(n, OVERWRITE_PRIVATE_EVENTFUL_OBJECT));
break;
}
}
}
}
}
/*
* Filter out any eventful objects returned.
*/
private void visitReturn(NodeTraversal t, Node n) {
Node variableNode = n.getFirstChild();
if (variableNode == null) {
return;
}
if (!variableNode.isArrayLit()) {
eventfulObjectDisposed(t, variableNode);
} else {
for (Node child : variableNode.children()) {
eventfulObjectDisposed(t, child);
}
}
}
/*
* Mark an eventful object as being disposed.
*/
private void eventfulObjectDisposed(NodeTraversal t, Node variableNode) {
String key = generateKey(t, variableNode, false);
if (key == null) {
return;
}
EventfulObjectState e = eventfulObjectMap.get(key);
if (e == null) {
e = new EventfulObjectState();
eventfulObjectMap.put(key, e);
}
e.seen = SeenType.POSSIBLY_DISPOSED;
}
@Override
public void enterScope(NodeTraversal t) {
/*
* Local variables captured in scope are filtered at present.
* LiveVariableAnalysis used to filter such variables.
*/
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, t.getScope(), compiler);
liveness.analyze();
for (Var v : liveness.getEscapedLocals()) {
eventfulObjectDisposed(t, v.getNode());
}
}
@Override
public void exitScope(NodeTraversal t) {
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.ASSIGN:
visitAssign(t, n);
break;
case Token.CALL:
visitCall(t, n);
break;
case Token.FUNCTION:
visitFunction(t, n);
break;
case Token.NEW:
visitNew(t, n, parent);
break;
case Token.RETURN:
visitReturn(t, n);
break;
default:
break;
}
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.TRUE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* Void type whose only element is the {@code undefined} value.
*/
public class VoidType extends ValueType {
private static final long serialVersionUID = 1L;
VoidType(JSTypeRegistry registry) {
super(registry);
}
@Override
public JSType restrictByNotNullOrUndefined() {
return registry.getNativeType(JSTypeNative.NO_TYPE);
}
@Override
public TernaryValue testForEquality(JSType that) {
if (UNKNOWN.equals(super.testForEquality(that))) {
return UNKNOWN;
}
if (that.isSubtype(this) ||
that.isSubtype(getNativeType(JSTypeNative.NULL_TYPE))) {
return TRUE;
}
return FALSE;
}
@Override
public boolean matchesNumberContext() {
return false;
}
@Override
public boolean matchesObjectContext() {
return false;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean isVoidType() {
return true;
}
@Override
String toStringHelper(boolean forAnnotations
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>) {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "undefined";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.FALSE;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseVoidType();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2005 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.javascript.jscomp.deps.DependencyInfo;
import com.google.javascript.jscomp.deps.SortedDependencies;
import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* A JavaScript module has a unique name, consists of a list of compiler inputs,
* and can depend on other modules.
*
*/
public class JSModule implements DependencyInfo, Serializable {
private static final long serialVersionUID = 1;
static final DiagnosticType CIRCULAR_DEPENDENCY_ERROR =
DiagnosticType.error("JSC_CIRCULAR_DEP",
"Circular dependency detected: {0}");
/** Module name */
private final String name;
/** Source code inputs */
private final List<CompilerInput> inputs = new ArrayList<CompilerInput>();
/** Modules that this module depends on */
private final List<JSModule> deps = new ArrayList<JSModule>();
private int depth;
/**
* Creates an instance.
*
* @param name A unique name for the module
*/
public JSModule(String name) {
this.name = name;
this.depth = -1;
}
/** Gets the module name. */
@Override
public String getName() {
return name;
}
@Override
public List<String> getProvides() {
return ImmutableList.<String>of(name);
}
@Override
public List<String> getRequires() {
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (JSModule m : deps) {
builder.add(m.getName());
}
return builder.build();
}
@Override
public String getPathRelativeToClosureBase() {
throw new UnsupportedOperationException();
}
/** Adds a source file input to this module. */
public void add(SourceFile file) {
add(new CompilerInput(file));
}
/** Adds a source file input to this module. */
public void addFirst(SourceFile file) {
addFirst(new CompilerInput(file));
}
/** Adds a source code input to this module. */
public void add(CompilerInput input
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>) {
inputs.add(input);
input.setModule(this);
}
/**
* Adds a source code input to this module. Call only if the input might
* already be associated with a module. Otherwise, use
* add(CompilerInput input).
*/
void addAndOverrideModule(CompilerInput input) {
inputs.add(input);
input.overrideModule(this);
}
/** Adds a source code input to this module. */
public void addFirst(CompilerInput input) {
inputs.add(0, input);
input.setModule(this);
}
/** Adds a source code input to this module directly after other. */
public void addAfter(CompilerInput input, CompilerInput other) {
Preconditions.checkState(inputs.contains(other));
inputs.add(inputs.indexOf(other), input);
input.setModule(this);
}
/** Adds a dependency on another module. */
public void addDependency(JSModule dep) {
Preconditions.checkNotNull(dep);
Preconditions.checkState(dep != this);
deps.add(dep);
}
/** Removes an input from this module. */
public void remove(CompilerInput input) {
input.setModule(null);
inputs.remove(input);
}
/** Removes all of the inputs from this module. */
public void removeAll() {
for (CompilerInput input : inputs) {
input.setModule(null);
}
inputs.clear();
}
/**
* Gets the list of modules that this module depends on.
*
* @return A list that may be empty but not null
*/
public List<JSModule> getDependencies() {
return deps;
}
/**
* Gets the names of the modules that this module depends on,
* sorted alphabetically.
*/
List<String> getSortedDependencyNames() {
List<String> names = Lists.newArrayList();
for (JSModule module : getDependencies()) {
names.add(module.getName());
}
Collections.sort(names);
return names;
}
/**
* Returns the transitive closure of dependencies starting from the
* dependencies of this module.
*/
public Set<JSModule> getAllDependencies() {
Set<JSModule> allDeps = Sets.newHashSet(deps);
List<JSModule> workList = Lists.newArrayList(deps);
while (workList.size() > 0) {
JSModule module = workList.remove(workList.size() - 1);
for (JSModule dep : module.getDependencies()) {
if (allDeps.add(dep)) {
workList.add(dep);
}
}
}
return allDeps;
}
/** Returns this module and all of its dependencies in one list. */
public Set<JSModule> getThisAndAllDependencies() {
Set<JSModule> deps = getAllDependencies();
deps.add(this);
return deps;
}
/**
* Gets this module's list of source code inputs.
*
* @return A list that may be empty but not null
*/
public List<CompilerInput> getInputs() {
return inputs;
}
/** Returns the input with the given name or null if none. */
public CompilerInput getByName(String name) {
for (
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>CompilerInput input : inputs) {
if (name.equals(input.getName())) {
return input;
}
}
return null;
}
/**
* Removes any input with the given name. Returns whether any were removed.
*/
public boolean removeByName(String name) {
boolean found = false;
Iterator<CompilerInput> iter = inputs.iterator();
while (iter.hasNext()) {
CompilerInput file = iter.next();
if (name.equals(file.getName())) {
iter.remove();
file.setModule(null);
found = true;
}
}
return found;
}
/** Returns the module name (primarily for debugging). */
@Override
public String toString() {
return name;
}
/**
* Removes any references to nodes of the AST. This method is needed to
* allow the ASTs to be garbage collected if the modules are kept around.
*/
public void clearAsts() {
for (CompilerInput input : inputs) {
input.clearAst();
}
}
/**
* Puts the JS files into a topologically sorted order by their dependencies.
*/
public void sortInputsByDeps(Compiler compiler) {
// Set the compiler, so that we can parse requires/provides and report
// errors properly.
for (CompilerInput input : inputs) {
input.setCompiler(compiler);
}
// Sort the JSModule in this order.
try {
List<CompilerInput> sortedList =
(new SortedDependencies<CompilerInput>(
Collections.<CompilerInput>unmodifiableList(inputs)))
.getSortedList();
inputs.clear();
inputs.addAll(sortedList);
} catch (CircularDependencyException e) {
compiler.report(
JSError.make(CIRCULAR_DEPENDENCY_ERROR, e.getMessage()));
}
}
/**
* Returns the given collection of modules in topological order.
*
* Note that this will return the modules in the same order if they are
* already sorted, and in general, will only change the order as necessary to
* satisfy the ordering dependencies. This can be important for cases where
* the modules do not properly specify all dependencies.
*/
public static JSModule[] sortJsModules(Collection<JSModule> modules)
throws CircularDependencyException {
// Sort the JSModule in this order.
List<JSModule> sortedList = (new SortedDependencies<JSModule>(
Lists.newArrayList(modules))).getSortedList();
return sortedList.toArray(new JSModule[sortedList.size()]);
}
/**
* @param dep the depth to set
*/
public void setDepth(int dep) {
this.depth = dep;
}
/**
* @return the depth
*/
public int getDepth() {
return depth;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.javascript.jscomp.NodeTraversal.ScopedCallback;
import com.google.javascript.jscomp.Scope.Var;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.StaticReference;
import com.google.javascript.rhino.jstype.StaticSourceFile;
import com.google.javascript.rhino.jstype.StaticSymbolTable;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A helper class for passes that want to access all information about where a
* variable is referenced and declared at once and then make a decision as to
* how it should be handled, possibly inlining, reordering, or generating
* warnings. Callers do this by providing {@link Behavior} and then
* calling {@link #process(Node, Node)}.
*
* @author kushal@google.com (Kushal Dave)
*/
class ReferenceCollectingCallback implements ScopedCallback,
HotSwapCompilerPass,
StaticSymbolTable<Var, ReferenceCollectingCallback.Reference> {
/**
* Maps a given variable to a collection of references to that name. Note that
* Var objects are not stable across multiple traversals (unlike scope root or
* name).
*/
private final Map<Var, ReferenceCollection> referenceMap =
Maps.newHashMap();
/**
* The stack of basic blocks and scopes the current traversal is in.
*/
private final Deque<BasicBlock> blockStack = new ArrayDeque<BasicBlock>();
/**
* Source of behavior at various points in the traversal.
*/
private final Behavior behavior;
/**
* JavaScript compiler to use in traversing.
*/
private final AbstractCompiler compiler;
/**
* Only collect references for filtered variables.
*/
private final Predicate<
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Var> varFilter;
/**
* Constructor initializes block stack.
*/
ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior) {
this(compiler, behavior, Predicates.<Var>alwaysTrue());
}
/**
* Constructor only collects references that match the given variable.
*
* The test for Var equality uses reference equality, so it's necessary to
* inject a scope when you traverse.
*/
ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior,
Predicate<Var> varFilter) {
this.compiler = compiler;
this.behavior = behavior;
this.varFilter = varFilter;
}
/**
* Convenience method for running this pass over a tree with this
* class as a callback.
*/
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
}
/**
* Same as process but only runs on a part of AST associated to one script.
*/
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
NodeTraversal.traverse(compiler, scriptRoot, this);
}
/**
* Gets the variables that were referenced in this callback.
*/
@Override
public Iterable<Var> getAllSymbols() {
return referenceMap.keySet();
}
@Override
public Scope getScope(Var var) {
return var.scope;
}
/**
* Gets the reference collection for the given variable.
*/
@Override
public ReferenceCollection getReferences(Var v) {
return referenceMap.get(v);
}
/**
* For each node, update the block stack and reference collection
* as appropriate.
*/
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
Var v;
if (n.getString().equals("arguments")) {
v = t.getScope().getArgumentsVar();
} else {
v = t.getScope().getVar(n.getString());
}
if (v != null && varFilter.apply(v)) {
addReference(v, new Reference(n, t, blockStack.peek()));
}
}
if (isBlockBoundary(n, parent)) {
blockStack.pop();
}
}
/**
* Updates block stack and invokes any additional behavior.
*/
@Override
public void enterScope(NodeTraversal t) {
Node n = t.getScope().getRootNode();
BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek();
blockStack.push(new BasicBlock(parent, n));
}
/**
* Updates block stack and invokes any additional behavior.
*/
@Override
public void exitScope(NodeTraversal t) {
blockStack.pop();
if (t.getScope().isGlobal()) {
// Update global scope reference lists when we are done with it.
compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot());
behavior.afterExitScope(t, compiler.getGlobalVarReferences());
} else {
behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap));
}
}
/**
* Updates block stack.
*/
@Override
public boolean shouldTraverse(NodeTraversal
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> nodeTraversal, Node n,
Node parent) {
// If node is a new basic block, put on basic block stack
if (isBlockBoundary(n, parent)) {
blockStack.push(new BasicBlock(blockStack.peek(), n));
}
return true;
}
/**
* @return true if this node marks the start of a new basic block
*/
private static boolean isBlockBoundary(Node n, Node parent) {
if (parent != null) {
switch (parent.getType()) {
case Token.DO:
case Token.FOR:
case Token.TRY:
case Token.WHILE:
case Token.WITH:
// NOTE: TRY has up to 3 child blocks:
// TRY
// BLOCK
// BLOCK
// CATCH
// BLOCK
// Note that there is an explicit CATCH token but no explicit
// FINALLY token. For simplicity, we consider each BLOCK
// a separate basic BLOCK.
return true;
case Token.AND:
case Token.HOOK:
case Token.IF:
case Token.OR:
// The first child of a conditional is not a boundary,
// but all the rest of the children are.
return n != parent.getFirstChild();
}
}
return n.isCase();
}
private void addReference(Var v, Reference reference) {
// Create collection if none already
ReferenceCollection referenceInfo = referenceMap.get(v);
if (referenceInfo == null) {
referenceInfo = new ReferenceCollection();
referenceMap.put(v, referenceInfo);
}
// Add this particular reference
referenceInfo.add(reference);
}
interface ReferenceMap {
ReferenceCollection getReferences(Var var);
}
private static class ReferenceMapWrapper implements ReferenceMap {
private final Map<Var, ReferenceCollection> referenceMap;
public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) {
this.referenceMap = referenceMap;
}
@Override
public ReferenceCollection getReferences(Var var) {
return referenceMap.get(var);
}
}
/**
* Way for callers to add specific behavior during traversal that
* utilizes the built-up reference information.
*/
interface Behavior {
/**
* Called after we finish with a scope.
*/
void afterExitScope(NodeTraversal t, ReferenceMap referenceMap);
}
static final Behavior DO_NOTHING_BEHAVIOR = new Behavior() {
@Override
public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {}
};
/**
* A collection of references. Can be subclassed to apply checks or
* store additional state when adding.
*/
static class ReferenceCollection implements Iterable<Reference> {
List<Reference> references = Lists.newArrayList();
@Override
public Iterator<Reference> iterator() {
return references.iterator();
}
void add(Reference reference) {
references.add(reference);
}
/**
* Determines if the variable for this reference collection is
* "well-defined." A variable is well-defined if we can prove at
* compile-time that it's assigned a value before it's used.
*
* Notice that if this function returns false, this doesn't imply that the
* variable is used before it's assigned. It just means
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
input.getInputId());
}
private Reference(Node nameNode,
BasicBlock basicBlock, Scope scope, InputId inputId) {
this.nameNode = nameNode;
this.basicBlock = basicBlock;
this.scope = scope;
this.inputId = inputId;
this.sourceFile = nameNode.getStaticSourceFile();
}
/**
* Makes a copy of the current reference using a new Scope instance.
*/
Reference cloneWithNewScope(Scope newScope) {
return new Reference(nameNode, basicBlock, newScope, inputId);
}
@Override
public Var getSymbol() {
return scope.getVar(nameNode.getString());
}
@Override
public Node getNode() {
return nameNode;
}
public InputId getInputId() {
return inputId;
}
@Override
public StaticSourceFile getSourceFile() {
return sourceFile;
}
boolean isDeclaration() {
Node parent = getParent();
Node grandparent = parent.getParent();
return DECLARATION_PARENTS.contains(parent.getType()) ||
parent.isParamList() &&
grandparent.isFunction();
}
boolean isVarDeclaration() {
return getParent().isVar();
}
boolean isHoistedFunction() {
return NodeUtil.isHoistedFunctionDeclaration(getParent());
}
/**
* Determines whether the variable is initialized at the declaration.
*/
boolean isInitializingDeclaration() {
// VAR is the only type of variable declaration that may not initialize
// its variable. Catch blocks, named functions, and parameters all do.
return isDeclaration() &&
!getParent().isVar() ||
nameNode.getFirstChild() != null;
}
/**
* @return For an assignment, variable declaration, or function declaration
* return the assigned value, otherwise null.
*/
Node getAssignedValue() {
Node parent = getParent();
return (parent.isFunction())
? parent : NodeUtil.getAssignedValue(nameNode);
}
BasicBlock getBasicBlock() {
return basicBlock;
}
Node getParent() {
return getNode().getParent();
}
Node getGrandparent() {
Node parent = getParent();
return parent == null ? null : parent.getParent();
}
private static boolean isLhsOfForInExpression(Node n) {
Node parent = n.getParent();
if (parent.isVar()) {
return isLhsOfForInExpression(parent);
}
return NodeUtil.isForIn(parent) && parent.getFirstChild() == n;
}
boolean isSimpleAssignmentToName() {
Node parent = getParent();
return parent.isAssign()
&& parent.getFirstChild() == nameNode;
}
boolean isLvalue() {
Node parent = getParent();
int parentType = parent.getType();
return (parentType == Token.VAR && nameNode.getFirstChild() != null)
|| parentType == Token.INC
|| parentType == Token.DEC
|| (NodeUtil.isAssignmentOp(parent)
&& parent.getFirstChild() == nameNode)
|| isLhsOfForInExpression(nameNode);
}
Scope getScope() {
return scope;
}
}
/**
* Represents a section of code that is uninterrupted by control structures
* (conditional or iter
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
private InferJSDocInfo inferJSDocInfo = null;
// These fields are used to calculate the percentage of expressions typed.
private int typedCount = 0;
private int nullCount = 0;
private int unknownCount = 0;
private boolean inExterns;
// A state boolean to see we are currently in @notypecheck section of the
// code.
private int noTypeCheckSection = 0;
private Method editDistance;
private static final class SuggestionPair {
private final String suggestion;
final int distance;
private SuggestionPair(String suggestion, int distance) {
this.suggestion = suggestion;
this.distance = distance;
}
}
public TypeCheck(AbstractCompiler compiler,
ReverseAbstractInterpreter reverseInterpreter,
JSTypeRegistry typeRegistry,
Scope topScope,
MemoizedScopeCreator scopeCreator,
CheckLevel reportMissingOverride) {
this.compiler = compiler;
this.validator = compiler.getTypeValidator();
this.reverseInterpreter = reverseInterpreter;
this.typeRegistry = typeRegistry;
this.topScope = topScope;
this.scopeCreator = scopeCreator;
this.reportMissingOverride = reportMissingOverride;
this.reportUnknownTypes = ((Compiler) compiler).getOptions().enables(
DiagnosticGroups.REPORT_UNKNOWN_TYPES);
this.inferJSDocInfo = new InferJSDocInfo(compiler);
ClassLoader classLoader = TypeCheck.class.getClassLoader();
try {
Class<?> c = classLoader.loadClass(
"com.google.common.string.EditDistance");
editDistance = c.getDeclaredMethod(
"getEditDistance", String.class, String.class, boolean.class);
} catch (Exception ignored) {
editDistance = null;
}
}
public TypeCheck(AbstractCompiler compiler,
ReverseAbstractInterpreter reverseInterpreter,
JSTypeRegistry typeRegistry,
CheckLevel reportMissingOverride) {
this(compiler, reverseInterpreter, typeRegistry, null, null,
reportMissingOverride);
}
TypeCheck(AbstractCompiler compiler,
ReverseAbstractInterpreter reverseInterpreter,
JSTypeRegistry typeRegistry) {
this(compiler, reverseInterpreter, typeRegistry, null, null,
CheckLevel.WARNING);
}
/** Turn on the missing property check. Returns this for easy chaining. */
TypeCheck reportMissingProperties(boolean report) {
reportMissingProperties = report;
return this;
}
/**
* Main entry point for this phase of processing. This follows the pattern for
* JSCompiler phases.
*
* @param externsRoot The root of the externs parse tree.
* @param jsRoot The root of the input parse tree to be checked.
*/
@Override
public void process(Node externsRoot, Node jsRoot) {
Preconditions.checkNotNull(scopeCreator);
Preconditions.checkNotNull(topScope);
Node externsAndJs = jsRoot.getParent();
Preconditions.checkState(externsAndJs != null);
Preconditions.checkState(
externsRoot == null || externsAndJs.hasChild(externsRoot));
if (externsRoot != null) {
check(externsRoot, true);
}
check(jsRoot, false);
}
/** Main entry point of this phase for testing code. */
public Scope processForTesting(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Node externsRoot, Node jsRoot) {
Preconditions.checkState(scopeCreator == null);
Preconditions.checkState(topScope == null);
Preconditions.checkState(jsRoot.getParent() != null);
Node externsAndJsRoot = jsRoot.getParent();
scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler));
topScope = scopeCreator.createScope(externsAndJsRoot, null);
TypeInferencePass inference = new TypeInferencePass(compiler,
reverseInterpreter, topScope, scopeCreator);
inference.process(externsRoot, jsRoot);
process(externsRoot, jsRoot);
return topScope;
}
public void check(Node node, boolean externs) {
Preconditions.checkNotNull(node);
NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator);
inExterns = externs;
t.traverseWithScope(node, topScope);
if (externs) {
inferJSDocInfo.process(node, null);
} else {
inferJSDocInfo.process(null, node);
}
}
private void checkNoTypeCheckSection(Node n, boolean enterSection) {
switch (n.getType()) {
case Token.SCRIPT:
case Token.BLOCK:
case Token.VAR:
case Token.FUNCTION:
case Token.ASSIGN:
JSDocInfo info = n.getJSDocInfo();
if (info != null && info.isNoTypeCheck()) {
if (enterSection) {
noTypeCheckSection++;
} else {
noTypeCheckSection--;
}
}
validator.setShouldReport(noTypeCheckSection == 0);
break;
}
}
private void report(NodeTraversal t, Node n, DiagnosticType diagnosticType,
String... arguments) {
if (noTypeCheckSection == 0) {
t.report(n, diagnosticType, arguments);
}
}
@Override
public boolean shouldTraverse(
NodeTraversal t, Node n, Node parent) {
checkNoTypeCheckSection(n, true);
switch (n.getType()) {
case Token.FUNCTION:
// normal type checking
final Scope outerScope = t.getScope();
final String functionPrivateName = n.getFirstChild().getString();
if (functionPrivateName != null && functionPrivateName.length() > 0 &&
outerScope.isDeclared(functionPrivateName, false) &&
// Ideally, we would want to check whether the type in the scope
// differs from the type being defined, but then the extern
// redeclarations of built-in types generates spurious warnings.
!(outerScope.getVar(
functionPrivateName).getType() instanceof FunctionType)) {
report(t, n, FUNCTION_MASKS_VARIABLE, functionPrivateName);
}
// TODO(user): Only traverse the function's body. The function's
// name and arguments are traversed by the scope creator, and ideally
// should not be traversed by the type checker.
break;
}
return true;
}
/**
* This is the meat of the type checking. It is basically one big switch,
* with each case representing one type of parse tree node. The individual
* cases are usually pretty straightforward.
*
*
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of the node n.
*/
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
JSType childType;
JSType leftType, rightType;
Node left, right;
// To be explicitly set to false if the node is not typeable.
boolean typeable = true;
switch (n.getType()) {
case Token.CAST:
Node expr = n.getFirstChild();
JSType exprType = getJSType(expr);
JSType castType = getJSType(n);
// TODO(johnlenz): determine if we can limit object literals in some
// way.
if (!expr.isObjectLit()) {
validator.expectCanCast(t, n, castType, exprType);
}
ensureTyped(t, n, castType);
if (castType.isSubtype(exprType) || expr.isObjectLit()) {
expr.setJSType(castType);
}
break;
case Token.NAME:
typeable = visitName(t, n, parent);
break;
case Token.PARAM_LIST:
typeable = false;
break;
case Token.COMMA:
ensureTyped(t, n, getJSType(n.getLastChild()));
break;
case Token.TRUE:
case Token.FALSE:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.THIS:
ensureTyped(t, n, t.getScope().getTypeOfThis());
break;
case Token.NULL:
ensureTyped(t, n, NULL_TYPE);
break;
case Token.NUMBER:
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.STRING:
ensureTyped(t, n, STRING_TYPE);
break;
case Token.STRING_KEY:
typeable = false;
break;
case Token.GETTER_DEF:
case Token.SETTER_DEF:
// Object literal keys are handled with OBJECTLIT
break;
case Token.ARRAYLIT:
ensureTyped(t, n, ARRAY_TYPE);
break;
case Token.REGEXP:
ensureTyped(t, n, REGEXP_TYPE);
break;
case Token.GETPROP:
visitGetProp(t, n, parent);
typeable = !(parent.isAssign() &&
parent.getFirstChild() == n);
break;
case Token.GETELEM:
visitGetElem(t, n);
// The type of GETELEM is always unknown, so no point counting that.
// If that unknown leaks elsewhere (say by an assignment to another
// variable), then it will be counted.
typeable = false;
break;
case Token.VAR:
visitVar(t, n);
typeable = false;
break;
case Token.NEW:
visitNew(t, n);
break;
case Token.CALL:
visitCall(t, n);
typeable = !parent.isExprResult();
break;
case Token.RETURN:
visitReturn(t, n);
typeable = false
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.CASE:
JSType switchType = getJSType(parent.getFirstChild());
JSType caseType = getJSType(n.getFirstChild());
validator.expectSwitchMatchesCase(t, n, switchType, caseType);
typeable = false;
break;
case Token.WITH: {
Node child = n.getFirstChild();
childType = getJSType(child);
validator.expectObject(t, child, childType, "with requires an object");
typeable = false;
break;
}
case Token.FUNCTION:
visitFunction(t, n);
break;
// These nodes have no interesting type behavior.
case Token.LABEL:
case Token.LABEL_NAME:
case Token.SWITCH:
case Token.BREAK:
case Token.CATCH:
case Token.TRY:
case Token.SCRIPT:
case Token.EXPR_RESULT:
case Token.BLOCK:
case Token.EMPTY:
case Token.DEFAULT_CASE:
case Token.CONTINUE:
case Token.DEBUGGER:
case Token.THROW:
typeable = false;
break;
// These nodes require data flow analysis.
case Token.DO:
case Token.IF:
case Token.WHILE:
typeable = false;
break;
case Token.FOR:
if (NodeUtil.isForIn(n)) {
Node obj = n.getChildAtIndex(1);
if (getJSType(obj).isStruct()) {
report(t, obj, IN_USED_WITH_STRUCT);
}
}
typeable = false;
break;
// These nodes are typed during the type inference.
case Token.AND:
case Token.HOOK:
case Token.OBJECTLIT:
case Token.OR:
if (n.getJSType() != null) { // If we didn't run type inference.
ensureTyped(t, n);
} else {
// If this is an enum, then give that type to the objectlit as well.
if ((n.isObjectLit())
&& (parent.getJSType() instanceof EnumType)) {
ensureTyped(t, n, parent.getJSType());
} else {
ensureTyped(t, n);
}
}
if (n.isObjectLit()) {
JSType typ = getJSType(n);
for (Node key : n.children()) {
visitObjLitKey(t, key, n, typ);
}
}
break;
default:
report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType()));
ensureTyped(t, n);
break;
}
// Don't count externs since the user's code may not even use that part.
typeable = typeable && !inExterns;
if (typeable) {
doPercentTypedAccounting(t, n);
}
checkNoTypeCheckSection(n, false);
}
private void checkTypeofString(NodeTraversal t, Node n, String s) {
if (!(s.equals("number") || s.equals("string") || s.equals("boolean") ||
s.equals("undefined") || s.equals("function") ||
s.equals("object") || s
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> methods are implemented.
//
// As-is, this misses many other ways to override a property.
//
// object.prototype.property = ...;
if (object.isGetProp()) {
Node object2 = object.getFirstChild();
String property2 = NodeUtil.getStringValue(object.getLastChild());
if ("prototype".equals(property2)) {
JSType jsType = getJSType(object2);
if (jsType.isFunctionType()) {
FunctionType functionType = jsType.toMaybeFunctionType();
if (functionType.isConstructor() || functionType.isInterface()) {
checkDeclaredPropertyInheritance(
t, assign, functionType, property, info, propertyType);
}
}
}
}
}
/**
* Visits an object literal field definition <code>key : value</code>.
*
* If the <code>lvalue</code> is a prototype modification, we change the
* schema of the object type it is referring to.
*
* @param t the traversal
* @param key the assign node
*/
private void visitObjLitKey(
NodeTraversal t, Node key, Node objlit, JSType litType) {
// Do not validate object lit value types in externs. We don't really care,
// and it makes it easier to generate externs.
if (objlit.isFromExterns()) {
ensureTyped(t, key);
return;
}
// Structs must have unquoted keys and dicts must have quoted keys
if (litType.isStruct() && key.isQuotedString()) {
report(t, key, ILLEGAL_OBJLIT_KEY, "struct");
} else if (litType.isDict() && !key.isQuotedString()) {
report(t, key, ILLEGAL_OBJLIT_KEY, "dict");
}
// TODO(johnlenz): Validate get and set function declarations are valid
// as is the functions can have "extraneous" bits.
// For getter and setter property definitions the
// r-value type != the property type.
Node rvalue = key.getFirstChild();
JSType rightType = NodeUtil.getObjectLitKeyTypeFromValueType(
key, getJSType(rvalue));
if (rightType == null) {
rightType = getNativeType(UNKNOWN_TYPE);
}
Node owner = objlit;
// Validate value is assignable to the key type.
JSType keyType = getJSType(key);
JSType allowedValueType = keyType;
if (allowedValueType.isEnumElementType()) {
allowedValueType =
allowedValueType.toMaybeEnumElementType().getPrimitiveType();
}
boolean valid = validator.expectCanAssignToPropertyOf(t, key,
rightType, allowedValueType,
owner, NodeUtil.getObjectLitKeyName(key));
if (valid) {
ensureTyped(t, key, rightType);
} else {
ensureTyped(t, key);
}
// Validate that the key type is assignable to the object property type.
// This is necessary as the objlit may have been cast to a non-literal
// object type.
// TODO(johnlenz): consider introducing a CAST node to the AST (or
// perhaps a parentheses node).
JSType objlit
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Type = getJSType(objlit);
ObjectType type = ObjectType.cast(
objlitType.restrictByNotNullOrUndefined());
if (type != null) {
String property = NodeUtil.getObjectLitKeyName(key);
if (type.hasProperty(property) &&
!type.isPropertyTypeInferred(property) &&
!propertyIsImplicitCast(type, property)) {
validator.expectCanAssignToPropertyOf(
t, key, keyType,
type.getPropertyType(property), owner, property);
}
return;
}
}
/**
* Returns true if any type in the chain has an implicitCast annotation for
* the given property.
*/
private static boolean propertyIsImplicitCast(ObjectType type, String prop) {
for (; type != null; type = type.getImplicitPrototype()) {
JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop);
if (docInfo != null && docInfo.isImplicitCast()) {
return true;
}
}
return false;
}
/**
* Given a constructor type and a property name, check that the property has
* the JSDoc annotation @override iff the property is declared on a
* superclass. Several checks regarding inheritance correctness are also
* performed.
*/
private void checkDeclaredPropertyInheritance(
NodeTraversal t, Node n, FunctionType ctorType, String propertyName,
JSDocInfo info, JSType propertyType) {
// If the supertype doesn't resolve correctly, we've warned about this
// already.
if (hasUnknownOrEmptySupertype(ctorType)) {
return;
}
FunctionType superClass = ctorType.getSuperClassConstructor();
boolean superClassHasProperty = superClass != null &&
superClass.getInstanceType().hasProperty(propertyName);
boolean superClassHasDeclaredProperty = superClass != null &&
superClass.getInstanceType().isPropertyTypeDeclared(propertyName);
// For interface
boolean superInterfaceHasProperty = false;
boolean superInterfaceHasDeclaredProperty = false;
if (ctorType.isInterface()) {
for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) {
superInterfaceHasProperty =
superInterfaceHasProperty ||
interfaceType.hasProperty(propertyName);
superInterfaceHasDeclaredProperty =
superInterfaceHasDeclaredProperty ||
interfaceType.isPropertyTypeDeclared(propertyName);
}
}
boolean declaredOverride = info != null && info.isOverride();
boolean foundInterfaceProperty = false;
if (ctorType.isConstructor()) {
for (JSType implementedInterface :
ctorType.getAllImplementedInterfaces()) {
if (implementedInterface.isUnknownType() ||
implementedInterface.isEmptyType()) {
continue;
}
FunctionType interfaceType =
implementedInterface.toObjectType().getConstructor();
Preconditions.checkNotNull(interfaceType);
boolean interfaceHasProperty =
interfaceType.getPrototype().hasProperty(propertyName);
foundInterfaceProperty = foundInterfaceProperty ||
interfaceHasProperty;
if (reportMissingOverride.isOn()
&& !declaredOverride
&& interfaceHasProperty) {
// @override not present, but the property does override an interface
// property
compiler.report(t.makeError(n, reportMissingOverride,
HIDDEN_INTERFACE_PROPERTY, propertyName,
interfaceType.getTopMostDefiningType(propertyName).toString()));
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
/**
* An unresolved type that was forward declared. So we know it exists,
* but that it wasn't pulled into this binary.
*
* In most cases, it behaves like a bottom type in the type lattice:
* no real type should be assigned to a NoResolvedType, but the
* NoResolvedType is a subtype of everything. In a few cases, it behaves
* like the unknown type: properties of this type are also NoResolved types,
* and comparisons to other types always have an unknown result.
*
* @author nicksantos@google.com (Nick Santos)
*/
class NoResolvedType extends NoType {
private static final long serialVersionUID = 1L;
NoResolvedType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNoResolvedType() {
return true;
}
@Override
public boolean isNoType() {
return false;
}
@Override
public boolean isConstructor() {
return false;
}
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtypeHelper(this, that)) {
return true;
} else {
return !that.isNoType();
}
}
@Override
String toStringHelper(boolean forAnnotations) {
return forAnnotations ? "?" : "NoResolvedType";
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2010 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.ImmutableSet;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.jscomp.regex.RegExpTree;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.Set;
/**
* Look for references to the global RegExp object that would cause
* regular expressions to be unoptimizable, and checks that regular expressions
* are syntactically valid.
*
* @author johnlenz@google.com (John Lenz)
*/
class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass {
static final DiagnosticType REGEXP_REFERENCE =
DiagnosticType.warning("JSC_REGEXP_REFERENCE",
"References to the global RegExp object prevents " +
"optimization of regular expressions.");
static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning(
"JSC_MALFORMED_REGEXP",
"Malformed Regular Expression: {0}");
private static final Set<String> REGEXP_PROPERTY_BLACKLIST = ImmutableSet.of(
"$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9",
"$_", "$input",
// The following would also be blacklisted, but they aren't valid
// identifiers, so can't be accessed with the '.' operator anyway.
// "$*", "$&", "$+", "$`", "$'",
"input", "lastMatch", "lastParen", "leftContext", "rightContext",
"global", "ignoreCase", "lastIndex", "multiline", "source");
private final AbstractCompiler compiler;
private boolean globalRegExpPropertiesUsed = false;
public boolean isGlobalRegExpPropertiesUsed() {
return globalRegExpPropertiesUsed;
}
public CheckRegExp(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (NodeUtil.isReferenceName(n)) {
String name = n.getString();
if (name.equals("RegExp") && t.getScope().getVar(name) == null) {
int parentType = parent.getType();
boolean first = (n == parent.getFirstChild());
if (!((parentType == Token.NEW && first)
|| (parentType == Token.CALL && first)
|| (
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>parentType == Token.INSTANCEOF && !first)
|| parentType == Token.EQ || parentType == Token.NE
|| parentType == Token.SHEQ || parentType == Token.SHNE
|| parentType == Token.CASE
|| (parentType == Token.GETPROP && first
&& !REGEXP_PROPERTY_BLACKLIST.contains(
parent.getLastChild().getString())))) {
t.report(n, REGEXP_REFERENCE);
globalRegExpPropertiesUsed = true;
}
}
// Check the syntax of regular expression patterns.
} else if (n.isRegExp()) {
String pattern = n.getFirstChild().getString();
String flags = n.getChildCount() == 2
? n.getLastChild().getString() : "";
try {
RegExpTree.parseRegExp(pattern, flags);
} catch (IllegalArgumentException ex) {
t.report(n, MALFORMED_REGEXP, ex.getMessage());
} catch (IndexOutOfBoundsException ex) {
t.report(n, MALFORMED_REGEXP, ex.getMessage());
}
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
if (comment.getCommentType() == CommentType.JSDOC &&
!irFactory.parsedComments.contains(comment)) {
irFactory.handlePossibleFileOverviewJsDoc(comment, irNode);
} else if (comment.getCommentType() == CommentType.BLOCK_COMMENT) {
irFactory.handleBlockComment(comment);
}
}
}
irFactory.setFileOverviewJsDoc(irNode);
return irNode;
}
private void setFileOverviewJsDoc(Node irNode) {
// Only after we've seen all @fileoverview entries, attach the
// last one to the root node, and copy the found license strings
// to that node.
JSDocInfo rootNodeJsDoc = rootNodeJsDocHolder.getJSDocInfo();
if (rootNodeJsDoc != null) {
irNode.setJSDocInfo(rootNodeJsDoc);
rootNodeJsDoc.setAssociatedNode(irNode);
}
if (fileOverviewInfo != null) {
if ((irNode.getJSDocInfo() != null) &&
(irNode.getJSDocInfo().getLicense() != null)) {
fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense());
}
irNode.setJSDocInfo(fileOverviewInfo);
fileOverviewInfo.setAssociatedNode(irNode);
}
}
private Node transformBlock(AstNode node) {
Node irNode = transform(node);
if (!irNode.isBlock()) {
if (irNode.isEmpty()) {
irNode.setType(Token.BLOCK);
irNode.setWasEmptyNode(true);
} else {
Node newBlock = newNode(Token.BLOCK, irNode);
newBlock.setLineno(irNode.getLineno());
newBlock.setCharno(irNode.getCharno());
maybeSetLengthFrom(newBlock, node);
irNode = newBlock;
}
}
return irNode;
}
/**
* Check to see if the given block comment looks like it should be JSDoc.
*/
private void handleBlockComment(Comment comment) {
Pattern p = Pattern.compile("(/|(\n[ \t]*))\\*[ \t]*@[a-zA-Z]+[ \t\n{]");
if (p.matcher(comment.getValue()).find()) {
errorReporter.warning(
SUSPICIOUS_COMMENT_WARNING,
sourceName,
comment.getLineno(), "", 0);
}
}
/**
* @return true if the jsDocParser represents a fileoverview.
*/
private boolean handlePossibleFileOverviewJsDoc(
JsDocInfoParser jsDocParser) {
if (jsDocParser.getFileOverviewJSDocInfo() != fileOverviewInfo) {
fileOverviewInfo = jsDocParser.getFileOverviewJSDocInfo();
return true;
}
return false;
}
private void handlePossibleFileOverviewJsDoc(Comment comment, Node irNode) {
JsDocInfoParser jsDocParser = createJsDocInfoParser(comment, irNode);
parsedComments.add(comment);
handlePossibleFileOverviewJsDoc(jsDocParser);
}
private JSDocInfo handleJsDoc(AstNode node,
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> node = newNode(transformTokenType(n.getType()));
for (com.google.javascript.rhino.head.Node child : n) {
node.addChildToBack(transform((AstNode) child));
}
return node;
}
/**
* Transforms the given node and then sets its type to Token.STRING if it
* was Token.NAME. If its type was already Token.STRING, then quotes it.
* Used for properties, as the old AST uses String tokens, while the new one
* uses Name tokens for unquoted strings. For example, in
* var o = {'a' : 1, b: 2};
* the string 'a' is quoted, while the name b is turned into a string, but
* unquoted.
*/
private Node transformAsString(AstNode n) {
Node ret;
if (n instanceof Name) {
ret = transformNameAsString((Name) n);
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral) n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
} else {
ret = transform(n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
}
Preconditions.checkState(ret.isString());
return ret;
}
@Override
Node processArrayLiteral(ArrayLiteral literalNode) {
if (literalNode.isDestructuring()) {
reportDestructuringAssign(literalNode);
}
Node node = newNode(Token.ARRAYLIT);
for (AstNode child : literalNode.getElements()) {
Node c = transform(child);
node.addChildToBack(c);
}
return node;
}
@Override
Node processAssignment(Assignment assignmentNode) {
Node assign = processInfixExpression(assignmentNode);
Node target = assign.getFirstChild();
if (!validAssignmentTarget(target)) {
errorReporter.error(
"invalid assignment target",
sourceName,
target.getLineno(), "", 0);
}
return assign;
}
@Override
Node processAstRoot(AstRoot rootNode) {
Node node = newNode(Token.SCRIPT);
for (com.google.javascript.rhino.head.Node child : rootNode) {
node.addChildToBack(transform((AstNode) child));
}
parseDirectives(node);
return node;
}
/**
* Parse the directives, encode them in the AST, and remove their nodes.
*
* For information on ES5 directives, see section 14.1 of
* ECMA-262, Edition 5.
*
* It would be nice if Rhino would eventually take care of this for
* us, but right now their directive-processing is a one-off.
*/
private void parseDirectives(Node node) {
// Remove all the directives, and encode them in the AST.
Set<String> directives = null;
while (isDirective(node.getFirstChild())) {
String directive = node.removeFirstChild().getFirstChild().getString();
if (directives == null) {
directives = Sets.newHashSet(directive);
} else {
directives.add(directive);
}
}
if (directives != null) {
node.setDirectives(directives);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
}
}
private boolean isDirective(Node n) {
if (n == null) {
return false;
}
int nType = n.getType();
return nType == Token.EXPR_RESULT &&
n.getFirstChild().isString() &&
ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString());
}
@Override
Node processBlock(Block blockNode) {
return processGeneric(blockNode);
}
@Override
Node processBreakStatement(BreakStatement statementNode) {
Node node = newNode(Token.BREAK);
if (statementNode.getBreakLabel() != null) {
Node labelName = transform(statementNode.getBreakLabel());
// Change the NAME to LABEL_NAME
labelName.setType(Token.LABEL_NAME);
node.addChildToBack(labelName);
}
return node;
}
@Override
Node processCatchClause(CatchClause clauseNode) {
AstNode catchVar = clauseNode.getVarName();
Node node = newNode(Token.CATCH, transform(catchVar));
if (clauseNode.getCatchCondition() != null) {
errorReporter.error(
"Catch clauses are not supported",
sourceName,
clauseNode.getCatchCondition().getLineno(), "", 0);
}
node.addChildToBack(transformBlock(clauseNode.getBody()));
return node;
}
@Override
Node processConditionalExpression(ConditionalExpression exprNode) {
return newNode(
Token.HOOK,
transform(exprNode.getTestExpression()),
transform(exprNode.getTrueExpression()),
transform(exprNode.getFalseExpression()));
}
@Override
Node processContinueStatement(ContinueStatement statementNode) {
Node node = newNode(Token.CONTINUE);
if (statementNode.getLabel() != null) {
Node labelName = transform(statementNode.getLabel());
// Change the NAME to LABEL_NAME
labelName.setType(Token.LABEL_NAME);
node.addChildToBack(labelName);
}
return node;
}
@Override
Node processDoLoop(DoLoop loopNode) {
return newNode(
Token.DO,
transformBlock(loopNode.getBody()),
transform(loopNode.getCondition()));
}
@Override
Node processElementGet(ElementGet getNode) {
return newNode(
Token.GETELEM,
transform(getNode.getTarget()),
transform(getNode.getElement()));
}
@Override
Node processEmptyExpression(EmptyExpression exprNode) {
Node node = newNode(Token.EMPTY);
return node;
}
@Override
Node processEmptyStatement(EmptyStatement exprNode) {
Node node = newNode(Token.EMPTY);
return node;
}
@Override
Node processExpressionStatement(ExpressionStatement statementNode) {
Node node = newNode(transformTokenType(statementNode.getType()));
node.addChildToBack(transform(statementNode.getExpression()));
return node;
}
@Override
Node processForInLoop(ForInLoop loopNode) {
if (loopNode.isForEach()) {
errorReporter.error(
"unsupported language extension: for each",
sourceName,
loopNode.getLineno(), "", 0);
// Return the bare minimum to put the AST in a valid state.
return newNode(Token.EXPR_RESULT, Node.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>newNumber(0));
}
return newNode(
Token.FOR,
transform(loopNode.getIterator()),
transform(loopNode.getIteratedObject()),
transformBlock(loopNode.getBody()));
}
@Override
Node processForLoop(ForLoop loopNode) {
Node node = newNode(
Token.FOR,
transform(loopNode.getInitializer()),
transform(loopNode.getCondition()),
transform(loopNode.getIncrement()));
node.addChildToBack(transformBlock(loopNode.getBody()));
return node;
}
@Override
Node processFunctionCall(FunctionCall callNode) {
Node node = newNode(transformTokenType(callNode.getType()),
transform(callNode.getTarget()));
for (AstNode child : callNode.getArguments()) {
node.addChildToBack(transform(child));
}
node.setLineno(node.getFirstChild().getLineno());
node.setCharno(node.getFirstChild().getCharno());
maybeSetLengthFrom(node, callNode);
return node;
}
@Override
Node processFunctionNode(FunctionNode functionNode) {
Name name = functionNode.getFunctionName();
Boolean isUnnamedFunction = false;
if (name == null) {
int functionType = functionNode.getFunctionType();
if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
errorReporter.error(
"unnamed function statement",
sourceName,
functionNode.getLineno(), "", 0);
// Return the bare minimum to put the AST in a valid state.
return newNode(Token.EXPR_RESULT, Node.newNumber(0));
}
name = new Name();
name.setIdentifier("");
isUnnamedFunction = true;
}
Node node = newNode(Token.FUNCTION);
// if the function has an inline return annotation, attach it
Node newName = transformNodeWithInlineJsDoc(name);
if (isUnnamedFunction) {
// Old Rhino tagged the empty name node with the line number of the
// declaration.
newName.setLineno(functionNode.getLineno());
// TODO(bowdidge) Mark line number of paren correctly.
// Same problem as below - the left paren might not be on the
// same line as the function keyword.
int lpColumn = functionNode.getAbsolutePosition() +
functionNode.getLp();
newName.setCharno(position2charno(lpColumn));
maybeSetLengthFrom(newName, name);
}
node.addChildToBack(newName);
Node lp = newNode(Token.PARAM_LIST);
// The left paren's complicated because it's not represented by an
// AstNode, so there's nothing that has the actual line number that it
// appeared on. We know the paren has to appear on the same line as the
// function name (or else a semicolon will be inserted.) If there's no
// function name, assume the paren was on the same line as the function.
// TODO(bowdidge): Mark line number of paren correctly.
Name fnName = functionNode.getFunctionName();
if (fnName != null) {
lp.setLineno(fnName.getLineno());
} else {
lp.setLineno(functionNode.getLin
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>eno());
}
int lparenCharno = functionNode.getLp() +
functionNode.getAbsolutePosition();
lp.setCharno(position2charno(lparenCharno));
for (AstNode param : functionNode.getParams()) {
Node paramNode = transformNodeWithInlineJsDoc(param);
// When in ideMode Rhino can generate a param list with only a single
// ErrorNode. This is transformed into an EMPTY node. Drop this node in
// ideMode to keep the AST in a valid state.
if (paramNode.isName()) {
lp.addChildToBack(paramNode);
} else {
// We expect this in ideMode or when there is an error handling
// destructuring parameter assignments which aren't supported
// (an error has already been reported).
Preconditions.checkState(
config.isIdeMode
|| paramNode.isObjectLit()
|| paramNode.isArrayLit());
}
}
node.addChildToBack(lp);
Node bodyNode = transform(functionNode.getBody());
if (!bodyNode.isBlock()) {
// When in ideMode Rhino tries to parse some constructs the compiler
// doesn't support, repair it here. see Rhino's
// Parser#parseFunctionBodyExpr.
Preconditions.checkState(config.isIdeMode);
bodyNode = IR.block();
}
parseDirectives(bodyNode);
node.addChildToBack(bodyNode);
return node;
}
@Override
Node processIfStatement(IfStatement statementNode) {
Node node = newNode(Token.IF);
node.addChildToBack(transform(statementNode.getCondition()));
node.addChildToBack(transformBlock(statementNode.getThenPart()));
if (statementNode.getElsePart() != null) {
node.addChildToBack(transformBlock(statementNode.getElsePart()));
}
return node;
}
@Override
Node processInfixExpression(InfixExpression exprNode) {
Node n = newNode(
transformTokenType(exprNode.getType()),
transform(exprNode.getLeft()),
transform(exprNode.getRight()));
n.setLineno(exprNode.getLineno());
n.setCharno(position2charno(exprNode.getAbsolutePosition()));
maybeSetLengthFrom(n, exprNode);
return n;
}
@Override
Node processKeywordLiteral(KeywordLiteral literalNode) {
return newNode(transformTokenType(literalNode.getType()));
}
@Override
Node processLabel(Label labelNode) {
return newStringNode(Token.LABEL_NAME, labelNode.getName());
}
@Override
Node processLabeledStatement(LabeledStatement statementNode) {
Node node = newNode(Token.LABEL);
Node prev = null;
Node cur = node;
for (Label label : statementNode.getLabels()) {
if (prev != null) {
prev.addChildToBack(cur);
}
cur.addChildToBack(transform(label));
cur.setLineno(label.getLineno());
maybeSetLengthFrom(cur, label);
int clauseAbsolutePosition =
position2charno(label.getAbsolutePosition());
cur.setCharno(clauseAbsolutePosition);
prev = cur;
cur = newNode(Token.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>LABEL);
}
prev.addChildToBack(transform(statementNode.getStatement()));
return node;
}
@Override
Node processName(Name nameNode) {
return processName(nameNode, false);
}
Node processName(Name nameNode, boolean asString) {
if (asString) {
return newStringNode(Token.STRING, nameNode.getIdentifier());
} else {
if (isReservedKeyword(nameNode.getIdentifier())) {
errorReporter.error(
"identifier is a reserved word",
sourceName,
nameNode.getLineno(), "", 0);
}
return newStringNode(Token.NAME, nameNode.getIdentifier());
}
}
private boolean isAllowedProp(String identifier) {
if (config.languageMode == LanguageMode.ECMASCRIPT3) {
return !TokenStream.isKeyword(identifier);
}
return true;
}
private boolean isReservedKeyword(String identifier) {
if (config.languageMode == LanguageMode.ECMASCRIPT3) {
return TokenStream.isKeyword(identifier);
}
return reservedKeywords != null && reservedKeywords.contains(identifier);
}
@Override
Node processNewExpression(NewExpression exprNode) {
Node node = newNode(
transformTokenType(exprNode.getType()),
transform(exprNode.getTarget()));
for (AstNode child : exprNode.getArguments()) {
node.addChildToBack(transform(child));
}
node.setLineno(exprNode.getLineno());
node.setCharno(position2charno(exprNode.getAbsolutePosition()));
maybeSetLengthFrom(node, exprNode);
return node;
}
@Override
Node processNumberLiteral(NumberLiteral literalNode) {
return newNumberNode(literalNode.getNumber());
}
@Override
Node processObjectLiteral(ObjectLiteral literalNode) {
if (literalNode.isDestructuring()) {
reportDestructuringAssign(literalNode);
}
Node node = newNode(Token.OBJECTLIT);
for (ObjectProperty el : literalNode.getElements()) {
if (config.languageMode == LanguageMode.ECMASCRIPT3) {
if (el.isGetter()) {
reportGetter(el);
continue;
} else if (el.isSetter()) {
reportSetter(el);
continue;
}
}
AstNode rawKey = el.getLeft();
Node key = transformAsString(rawKey);
key.setType(Token.STRING_KEY);
if (rawKey instanceof Name && !isAllowedProp(key.getString())) {
errorReporter.warning(INVALID_ES3_PROP_NAME, sourceName,
key.getLineno(), "", key.getCharno());
}
Node value = transform(el.getRight());
if (el.isGetter()) {
key.setType(Token.GETTER_DEF);
Preconditions.checkState(value.isFunction());
if (getFnParamNode(value).hasChildren()) {
reportGetterParam(el.getLeft());
}
} else if (el.isSetter()) {
key.setType(Token.SETTER_DEF);
Preconditions.checkState(value.isFunction());
if (!getFnParamNode(value).hasOneChild()) {
reportSetterParam(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>el.getLeft());
}
}
key.addChildToFront(value);
node.addChildToBack(key);
}
return node;
}
/**
* @param fnNode The function.
* @return The Node containing the Function parameters.
*/
Node getFnParamNode(Node fnNode) {
// Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ]
Preconditions.checkArgument(fnNode.isFunction());
return fnNode.getFirstChild().getNext();
}
@Override
Node processObjectProperty(ObjectProperty propertyNode) {
return processInfixExpression(propertyNode);
}
@Override
Node processParenthesizedExpression(ParenthesizedExpression exprNode) {
Node node = transform(exprNode.getExpression());
return node;
}
@Override
Node processPropertyGet(PropertyGet getNode) {
Node leftChild = transform(getNode.getTarget());
AstNode nodeProp = getNode.getProperty();
Node rightChild = transformAsString(nodeProp);
if (nodeProp instanceof Name && !isAllowedProp(
((Name) nodeProp).getIdentifier())) {
errorReporter.warning(INVALID_ES3_PROP_NAME, sourceName,
rightChild.getLineno(), "", rightChild.getCharno());
}
Node newNode = newNode(
Token.GETPROP, leftChild, rightChild);
newNode.setLineno(leftChild.getLineno());
newNode.setCharno(leftChild.getCharno());
maybeSetLengthFrom(newNode, getNode);
return newNode;
}
@Override
Node processRegExpLiteral(RegExpLiteral literalNode) {
Node literalStringNode = newStringNode(literalNode.getValue());
// assume it's on the same line.
literalStringNode.setLineno(literalNode.getLineno());
maybeSetLengthFrom(literalStringNode, literalNode);
Node node = newNode(Token.REGEXP, literalStringNode);
String flags = literalNode.getFlags();
if (flags != null && !flags.isEmpty()) {
Node flagsNode = newStringNode(flags);
// Assume the flags are on the same line as the literal node.
flagsNode.setLineno(literalNode.getLineno());
maybeSetLengthFrom(flagsNode, literalNode);
node.addChildToBack(flagsNode);
}
return node;
}
@Override
Node processReturnStatement(ReturnStatement statementNode) {
Node node = newNode(Token.RETURN);
if (statementNode.getReturnValue() != null) {
node.addChildToBack(transform(statementNode.getReturnValue()));
}
return node;
}
@Override
Node processScope(Scope scopeNode) {
return processGeneric(scopeNode);
}
@Override
Node processStringLiteral(StringLiteral literalNode) {
String value = literalNode.getValue();
Node n = newStringNode(value);
if (value.indexOf('\u000B') != -1) {
// NOTE(nicksantos): In JavaScript, there are 3 ways to
// represent a vertical tab: \v, \x0B, \u000B.
// The \v notation was added later, and is not understood
// on IE. So we need to preserve it as-is. This is really
//
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> obnoxious, because we do not have a good way to represent
// how the original string was encoded without making the
// representation of strings much more complicated.
//
// To handle this, we look at the original source test, and
// mark the string as \v-encoded or not. If a string is
// \v encoded, then all the vertical tabs in that string
// will be encoded with a \v.
int start = literalNode.getAbsolutePosition();
int end = start + literalNode.getLength();
if (start < sourceString.length() &&
(sourceString.substring(
start, Math.min(sourceString.length(), end))
.indexOf("\\v") != -1)) {
n.putBooleanProp(Node.SLASH_V, true);
}
}
return n;
}
@Override
Node processSwitchCase(SwitchCase caseNode) {
Node node;
if (caseNode.isDefault()) {
node = newNode(Token.DEFAULT_CASE);
} else {
AstNode expr = caseNode.getExpression();
node = newNode(Token.CASE, transform(expr));
}
Node block = newNode(Token.BLOCK);
block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true);
block.setLineno(caseNode.getLineno());
block.setCharno(position2charno(caseNode.getAbsolutePosition()));
maybeSetLengthFrom(block, caseNode);
if (caseNode.getStatements() != null) {
for (AstNode child : caseNode.getStatements()) {
block.addChildToBack(transform(child));
}
}
node.addChildToBack(block);
return node;
}
@Override
Node processSwitchStatement(SwitchStatement statementNode) {
Node node = newNode(Token.SWITCH,
transform(statementNode.getExpression()));
for (AstNode child : statementNode.getCases()) {
node.addChildToBack(transform(child));
}
return node;
}
@Override
Node processThrowStatement(ThrowStatement statementNode) {
return newNode(Token.THROW,
transform(statementNode.getExpression()));
}
@Override
Node processTryStatement(TryStatement statementNode) {
Node node = newNode(Token.TRY,
transformBlock(statementNode.getTryBlock()));
Node block = newNode(Token.BLOCK);
boolean lineSet = false;
for (CatchClause cc : statementNode.getCatchClauses()) {
// Mark the enclosing block at the same line as the first catch
// clause.
if (lineSet == false) {
block.setLineno(cc.getLineno());
maybeSetLengthFrom(block, cc);
lineSet = true;
}
block.addChildToBack(transform(cc));
}
node.addChildToBack(block);
AstNode finallyBlock = statementNode.getFinallyBlock();
if (finallyBlock != null) {
node.addChildToBack(transformBlock(finallyBlock));
}
// If we didn't set the line on the catch clause, then
// we've got an empty catch clause. Set its line to be the same
// as the finally block (to match Old Rhino's behavior.)
if ((lineSet ==
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> false) && (finallyBlock != null)) {
block.setLineno(finallyBlock.getLineno());
maybeSetLengthFrom(block, finallyBlock);
}
return node;
}
@Override
Node processUnaryExpression(UnaryExpression exprNode) {
int type = transformTokenType(exprNode.getType());
Node operand = transform(exprNode.getOperand());
if (type == Token.NEG && operand.isNumber()) {
operand.setDouble(-operand.getDouble());
return operand;
} else {
if (type == Token.DELPROP &&
!(operand.isGetProp() ||
operand.isGetElem() ||
operand.isName())) {
String msg =
"Invalid delete operand. Only properties can be deleted.";
errorReporter.error(
msg,
sourceName,
operand.getLineno(), "", 0);
} else if (type == Token.INC || type == Token.DEC) {
if (!validAssignmentTarget(operand)) {
String msg = (type == Token.INC)
? "invalid increment target"
: "invalid decrement target";
errorReporter.error(
msg,
sourceName,
operand.getLineno(), "", 0);
}
}
Node node = newNode(type, operand);
if (exprNode.isPostfix()) {
node.putBooleanProp(Node.INCRDECR_PROP, true);
}
return node;
}
}
private boolean validAssignmentTarget(Node target) {
switch (target.getType()) {
case Token.CAST: // CAST is a bit weird, but syntactically valid.
case Token.NAME:
case Token.GETPROP:
case Token.GETELEM:
return true;
}
return false;
}
@Override
Node processVariableDeclaration(VariableDeclaration declarationNode) {
if (!config.acceptConstKeyword && declarationNode.getType() ==
com.google.javascript.rhino.head.Token.CONST) {
processIllegalToken(declarationNode);
}
Node node = newNode(Token.VAR);
for (VariableInitializer child : declarationNode.getVariables()) {
node.addChildToBack(transform(child));
}
return node;
}
@Override
Node processVariableInitializer(VariableInitializer initializerNode) {
Node node;
Comment comment = initializerNode.getTarget().getJsDocNode();
// TODO(user): At some point, consider allowing only inline jsdocs for
// variable initializers
if (comment != null && !comment.getValue().contains("@")) {
node = transformNodeWithInlineJsDoc(initializerNode.getTarget());
} else {
node = transform(initializerNode.getTarget());
}
if (initializerNode.getInitializer() != null) {
Node initalizer = transform(initializerNode.getInitializer());
node.addChildToBack(initalizer);
}
return node;
}
@Override
Node processWhileLoop(WhileLoop loopNode) {
return newNode(
Token.WHILE,
transform(loopNode.getCondition()),
transformBlock(loopNode.getBody()));
}
@Override
Node processWithStatement(WithStatement statementNode) {
return newNode(
Token.WITH,
transform(statementNode.getExpression()),
transformBlock(statementNode.getStatement()));
}
@
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Override
Node processIllegalToken(AstNode node) {
errorReporter.error(
"Unsupported syntax: " +
com.google.javascript.rhino.head.Token.typeToName(
node.getType()),
sourceName,
node.getLineno(), "", 0);
return newNode(Token.EMPTY);
}
void reportDestructuringAssign(AstNode node) {
errorReporter.error(
"destructuring assignment forbidden",
sourceName,
node.getLineno(), "", 0);
}
void reportGetter(AstNode node) {
errorReporter.error(
GETTER_ERROR_MESSAGE,
sourceName,
node.getLineno(), "", 0);
}
void reportSetter(AstNode node) {
errorReporter.error(
SETTER_ERROR_MESSAGE,
sourceName,
node.getLineno(), "", 0);
}
void reportGetterParam(AstNode node) {
errorReporter.error(
"getters may not have parameters",
sourceName,
node.getLineno(), "", 0);
}
void reportSetterParam(AstNode node) {
errorReporter.error(
"setters must have exactly one parameter",
sourceName,
node.getLineno(), "", 0);
}
}
private static int transformTokenType(int token) {
switch (token) {
case com.google.javascript.rhino.head.Token.RETURN:
return Token.RETURN;
case com.google.javascript.rhino.head.Token.BITOR:
return Token.BITOR;
case com.google.javascript.rhino.head.Token.BITXOR:
return Token.BITXOR;
case com.google.javascript.rhino.head.Token.BITAND:
return Token.BITAND;
case com.google.javascript.rhino.head.Token.EQ:
return Token.EQ;
case com.google.javascript.rhino.head.Token.NE:
return Token.NE;
case com.google.javascript.rhino.head.Token.LT:
return Token.LT;
case com.google.javascript.rhino.head.Token.LE:
return Token.LE;
case com.google.javascript.rhino.head.Token.GT:
return Token.GT;
case com.google.javascript.rhino.head.Token.GE:
return Token.GE;
case com.google.javascript.rhino.head.Token.LSH:
return Token.LSH;
case com.google.javascript.rhino.head.Token.RSH:
return Token.RSH;
case com.google.javascript.rhino.head.Token.URSH:
return Token.URSH;
case com.google.javascript.rhino.head.Token.ADD:
return Token.ADD;
case com.google.javascript.rhino.head.Token.SUB:
return Token.SUB;
case com.google.javascript.rhino.head.Token.MUL:
return Token.MUL;
case com.google.javascript.rhino.head.Token.DIV:
return Token.DIV;
case com.google.javascript.rhino.head.Token.MOD:
return Token.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.ErrorReporter;
/**
* Value types (null, void, number, boolean, string).
*/
abstract class ValueType extends JSType {
ValueType(JSTypeRegistry registry) {
super(registry);
}
@Override
final JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
return this;
}
@Override
public boolean hasDisplayName() {
return true;
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseValueType(this, that);
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
/**
* Sets the level for a particular DiagnosticGroup.
* @author nicksantos@google.com (Nick Santos)
*/
public class DiagnosticGroupWarningsGuard extends WarningsGuard {
private static final long serialVersionUID = 1L;
private final DiagnosticGroup group;
private final CheckLevel level;
public DiagnosticGroupWarningsGuard(
DiagnosticGroup group, CheckLevel level) {
this.group = group;
this.level = level;
}
@Override
public CheckLevel level(JSError error) {
return group.matches(error) ? level : null;
}
@Override
public boolean disables(DiagnosticGroup otherGroup) {
return !level.isOn() && group.isSubGroup(otherGroup);
}
@Override
public boolean enables(DiagnosticGroup otherGroup) {
if (level.isOn()) {
for (DiagnosticType type : otherGroup.getTypes()) {
if (group.matches(type)) {
return true;
}
}
}
return false;
}
@Override
public String toString() {
return group + "(" + level + ")";
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import static com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt.LINE;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.javascript.jscomp.SourceExcerptProvider.ExcerptFormatter;
import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt;
/**
* Lightweight message formatter. The format of messages this formatter
* produces is very compact and to the point.
*
*/
public class LightweightMessageFormatter extends AbstractMessageFormatter {
private SourceExcerpt excerpt;
private static final ExcerptFormatter excerptFormatter =
new LineNumberingFormatter();
/**
* A constructor for when the client doesn't care about source information.
*/
private LightweightMessageFormatter() {
super(null);
this.excerpt = LINE;
}
public LightweightMessageFormatter(SourceExcerptProvider source) {
this(source, LINE);
}
public LightweightMessageFormatter(SourceExcerptProvider source,
SourceExcerpt excerpt) {
super(source);
Preconditions.checkNotNull(source);
this.excerpt = excerpt;
}
static LightweightMessageFormatter withoutSource() {
return new LightweightMessageFormatter();
}
@Override
public String formatError(JSError error) {
return format(error, false);
}
@Override
public String formatWarning(JSError warning) {
return format(warning, true);
}
private String format(JSError error, boolean warning) {
// extract source excerpt
SourceExcerptProvider source = getSource();
String sourceExcerpt = source == null ? null :
excerpt.get(
source, error.sourceName, error.lineNumber, excerptFormatter);
// formatting the message
StringBuilder b = new StringBuilder();
if (error.sourceName != null) {
b.append(error.sourceName);
if (error.lineNumber > 0) {
b.append(':');
b.append(error.lineNumber);
}
b.append(": ");
}
b.append(getLevelName(warning ? CheckLevel.WARNING : CheckLevel.ERROR));
b.append(" - ");
b.append(error.description);
b.append('\n');
if (sourceExcerpt != null) {
b.append(sourceExcerpt);
b.append('\n');
int charno = error.getCharno();
// padding equal to the excerpt and arrow at the end
// charno == sourceExpert.length
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>() means something is missing
// at the end of the line
if (excerpt.equals(LINE)
&& 0 <= charno && charno <= sourceExcerpt.length()) {
for (int i = 0; i < charno; i++) {
char c = sourceExcerpt.charAt(i);
if (Character.isWhitespace(c)) {
b.append(c);
} else {
b.append(' ');
}
}
b.append("^\n");
}
}
return b.toString();
}
/**
* Formats a region by appending line numbers in front, e.g.
* <pre> 9| if (foo) {
* 10| alert('bar');
* 11| }</pre>
* and return line excerpt without any modification.
*/
static class LineNumberingFormatter implements ExcerptFormatter {
@Override
public String formatLine(String line, int lineNumber) {
return line;
}
@Override
public String formatRegion(Region region) {
if (region == null) {
return null;
}
String code = region.getSourceExcerpt();
if (code.length() == 0) {
return null;
}
// max length of the number display
int numberLength = Integer.toString(region.getEndingLineNumber())
.length();
// formatting
StringBuilder builder = new StringBuilder(code.length() * 2);
int start = 0;
int end = code.indexOf('\n', start);
int lineNumber = region.getBeginningLineNumber();
while (start >= 0) {
// line extraction
String line;
if (end < 0) {
line = code.substring(start);
if (line.length() == 0) {
return builder.substring(0, builder.length() - 1);
}
} else {
line = code.substring(start, end);
}
builder.append(" ");
// nice spaces for the line number
int spaces = numberLength - Integer.toString(lineNumber).length();
builder.append(Strings.repeat(" ", spaces));
builder.append(lineNumber);
builder.append("| ");
// end & update
if (end < 0) {
builder.append(line);
start = -1;
} else {
builder.append(line);
builder.append('\n');
start = end + 1;
end = code.indexOf('\n', start);
lineNumber++;
}
}
return builder.toString();
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2010 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.deps;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Multiset;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
/**
* A sorted list of inputs with dependency information. Uses a stable
* topological sort to make sure that an input always comes after its
* dependencies.
*
* Also exposes other information about the inputs, like which inputs
* do not provide symbols.
*
* @author nicksantos@google.com (Nick Santos)
*/
public class SortedDependencies<INPUT extends DependencyInfo> {
private final List<INPUT> inputs;
// A topologically sorted list of the inputs.
private final List<INPUT> sortedList;
// A list of all the inputs that do not have provides.
private final List<INPUT> noProvides;
private final Map<String, INPUT> provideMap = Maps.newHashMap();
public SortedDependencies(List<INPUT> inputs)
throws CircularDependencyException {
this.inputs = Lists.newArrayList(inputs);
noProvides = Lists.newArrayList();
// Collect all symbols provided in these files.
for (INPUT input : inputs) {
Collection<String> currentProvides = input.getProvides();
if (currentProvides.isEmpty()) {
noProvides.add(input);
}
for (String provide : currentProvides) {
provideMap.put(provide, input);
}
}
// Get the direct dependencies.
final Multimap<INPUT, INPUT> deps = HashMultimap.create();
for (INPUT input : inputs) {
for (String req : input.getRequires()) {
INPUT dep = provideMap.get(req);
if (dep != null && dep != input) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> deps.put(input, dep);
}
}
}
// Sort the inputs by sucking in 0-in-degree nodes until we're done.
sortedList = topologicalStableSort(inputs, deps);
// The dependency graph of inputs has a cycle iff sortedList is a proper
// subset of inputs. Also, it has a cycle iff the subgraph
// (inputs - sortedList) has a cycle. It's fairly easy to prove this
// by the lemma that a graph has a cycle iff it has a subgraph where
// no nodes have out-degree 0. I'll leave the proof of this as an exercise
// to the reader.
if (sortedList.size() < inputs.size()) {
List<INPUT> subGraph = Lists.newArrayList(inputs);
subGraph.removeAll(sortedList);
throw new CircularDependencyException(
cycleToString(findCycle(subGraph, deps)));
}
}
/**
* Return the input that gives us the given symbol.
* @throws MissingProvideException An exception if there is no
* input for this symbol.
*/
public INPUT getInputProviding(String symbol)
throws MissingProvideException {
if (provideMap.containsKey(symbol)) {
return provideMap.get(symbol);
}
throw new MissingProvideException(symbol);
}
/**
* Return the input that gives us the given symbol, or null.
*/
public INPUT maybeGetInputProviding(String symbol) {
return provideMap.get(symbol);
}
/**
* Returns the first circular dependency found. Expressed as a list of
* items in reverse dependency order (the second element depends on the
* first, etc.).
*/
private List<INPUT> findCycle(
List<INPUT> subGraph, Multimap<INPUT, INPUT> deps) {
return findCycle(subGraph.get(0), Sets.<INPUT>newHashSet(subGraph),
deps, Sets.<INPUT>newHashSet());
}
private List<INPUT> findCycle(
INPUT current, Set<INPUT> subGraph, Multimap<INPUT, INPUT> deps,
Set<INPUT> covered) {
if (covered.add(current)) {
List<INPUT> cycle = findCycle(
findRequireInSubGraphOrFail(current, subGraph),
subGraph, deps, covered);
// Don't add the input to the list if the cycle has closed already.
if (cycle.get(0) != cycle.get(cycle.size() - 1)) {
cycle.add(current);
}
return cycle;
} else {
// Explicitly use the add() method, to prevent a generics constructor
// warning that is dumb. The condition it's protecting is
// obscure, and I think people have proposed that it be removed.
List<INPUT> cycle = Lists.<INPUT>newArrayList();
cycle.add(current);
return cycle;
}
}
private INPUT findRequireInSubGraphOrFail(INPUT input, Set<INPUT> subGraph) {
for (String symbol : input.getRequires()) {
INPUT candidate = provideMap.get(symbol);
if (subGraph.contains(candidate)) {
return candidate;
}
}
throw new IllegalStateException("no require found in subgraph");
}
/**
* @
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>param cycle A cycle in reverse-dependency order.
*/
private String cycleToString(List<INPUT> cycle) {
List<String> symbols = Lists.newArrayList();
for (int i = cycle.size() - 1; i >= 0; i--) {
symbols.add(cycle.get(i).getProvides().iterator().next());
}
symbols.add(symbols.get(0));
return Joiner.on(" -> ").join(symbols);
}
public List<INPUT> getSortedList() {
return Collections.<INPUT>unmodifiableList(sortedList);
}
/**
* Gets all the dependencies of the given roots. The inputs must be returned
* in a stable order. In other words, if A comes before B, and A does not
* transitively depend on B, then A must also come before B in the returned
* list.
*/
public List<INPUT> getSortedDependenciesOf(List<INPUT> roots) {
return getDependenciesOf(roots, true);
}
/**
* Gets all the dependencies of the given roots. The inputs must be returned
* in a stable order. In other words, if A comes before B, and A does not
* transitively depend on B, then A must also come before B in the returned
* list.
*
* @param sorted If true, get them in topologically sorted order. If false,
* get them in the original order they were passed to the compiler.
*/
public List<INPUT> getDependenciesOf(List<INPUT> roots, boolean sorted) {
Preconditions.checkArgument(inputs.containsAll(roots));
Set<INPUT> included = Sets.newHashSet();
Deque<INPUT> worklist = new ArrayDeque<INPUT>(roots);
while (!worklist.isEmpty()) {
INPUT current = worklist.pop();
if (included.add(current)) {
for (String req : current.getRequires()) {
INPUT dep = provideMap.get(req);
if (dep != null) {
worklist.add(dep);
}
}
}
}
ImmutableList.Builder<INPUT> builder = ImmutableList.builder();
for (INPUT current : (sorted ? sortedList : inputs)) {
if (included.contains(current)) {
builder.add(current);
}
}
return builder.build();
}
public List<INPUT> getInputsWithoutProvides() {
return Collections.<INPUT>unmodifiableList(noProvides);
}
private static <T> List<T> topologicalStableSort(
List<T> items, Multimap<T, T> deps) {
if (items.size() == 0) {
// Priority queue blows up if we give it a size of 0. Since we need
// to special case this either way, just bail out.
return Lists.newArrayList();
}
final Map<T, Integer> originalIndex = Maps.newHashMap();
for (int i = 0; i < items.size(); i++) {
originalIndex.put(items.get(i), i);
}
PriorityQueue<T> inDegreeZero = new PriorityQueue<T>(items.size(),
new Comparator<T>() {
@Override
public int compare(T a, T b) {
return originalIndex.get(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* John Lenz
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.javascript.rhino.Node;
import java.util.List;
/**
* A visitor implementation that enables type substitutions.
*
* @author johnlenz@google.com (John Lenz)
*/
public class ModificationVisitor implements Visitor<JSType> {
private final JSTypeRegistry registry;
public ModificationVisitor(JSTypeRegistry registry) {
this.registry = registry;
}
@Override
public JSType caseNoType() {
return getNativeType(JSTypeNative.NO_TYPE);
}
@Override
public JSType caseEnumElementType(EnumElementType type) {
return type;
}
@Override
public JSType caseAllType() {
return getNativeType(JSTypeNative.ALL_TYPE);
}
@Override
public JSType caseBooleanType() {
return getNativeType(JSTypeNative.BOOLEAN_TYPE);
}
@Override
public JSType caseNoObjectType() {
return getNativeType(JSTypeNative.NO_OBJECT_TYPE);
}
@Override
public JSType caseFunctionType(FunctionType type) {
if (isNativeFunctionType(type)) {
return type;
}
// TODO(johnlenz): remove this simplifying assumption...
if (!type.isOrdinaryFunction
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>()) {
return type;
}
boolean changed = false;
JSType beforeThis = type.getTypeOfThis();
JSType afterThis = coerseToThisType(beforeThis.visit(this));
if (beforeThis != afterThis) {
changed = true;
}
JSType beforeReturn = type.getReturnType();
JSType afterReturn = beforeReturn.visit(this);
if (beforeReturn != afterReturn) {
changed = true;
}
FunctionParamBuilder paramBuilder = new FunctionParamBuilder(registry);
for (Node paramNode : type.getParameters()) {
JSType beforeParamType = paramNode.getJSType();
JSType afterParamType = beforeParamType.visit(this);
if (beforeParamType != afterParamType) {
changed = true;
}
if (paramNode.isOptionalArg()) {
paramBuilder.addOptionalParams(afterParamType);
} else if (paramNode.isVarArgs()) {
paramBuilder.addVarArgs(afterParamType);
} else {
paramBuilder.addRequiredParams(afterParamType);
}
}
if (changed) {
FunctionBuilder builder = new FunctionBuilder(registry);
builder.withParams(paramBuilder);
builder.withReturnType(afterReturn);
builder.withTypeOfThis(afterThis);
builder.withTemplateKeys(type.getTemplateTypeMap().getUnfilledTemplateKeys());
return builder.build();
}
return type;
}
private JSType coerseToThisType(JSType type) {
return type != null ? type : registry.getNativeObjectType(
JSTypeNative.UNKNOWN_TYPE);
}
@Override
public JSType caseObjectType(ObjectType objType) {
return objType;
}
@Override
public JSType caseTemplatizedType(TemplatizedType type) {
boolean changed = false;
ObjectType beforeBaseType = type.getReferencedType();
ObjectType afterBaseType = ObjectType.cast(beforeBaseType.visit(this));
if (beforeBaseType != afterBaseType) {
changed = true;
}
ImmutableList.Builder<JSType> builder = ImmutableList.builder();
for (JSType beforeTemplateType : type.getTemplateTypes()) {
JSType afterTemplateType = beforeTemplateType.visit(this);
if (beforeTemplateType != afterTemplateType) {
changed = true;
}
builder.add(afterTemplateType);
}
if (changed) {
type = registry.createTemplatizedType(afterBaseType, builder.build());
}
return type;
}
@Override
public JSType caseUnknownType() {
return getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
@Override
public JSType caseNullType() {
return getNativeType(JSTypeNative.NULL_TYPE);
}
@Override
public JSType caseNumberType() {
return getNativeType(JSTypeNative.NUMBER_TYPE);
}
@Override
public JSType caseStringType() {
return getNativeType(JSTypeNative.STRING_TYPE);
}
@Override
public JSType caseVoidType() {
return getNativeType(JSTypeNative.VOID_TYPE);
}
@Override
public JSType caseUnionType(UnionType type) {
boolean changed = false;
List<JSType>
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> results = Lists.newArrayList();
for (JSType alternative : type.getAlternates()) {
JSType replacement = alternative.visit(this);
if (replacement != alternative) {
changed = true;
}
results.add(replacement);
}
if (changed) {
UnionTypeBuilder builder = new UnionTypeBuilder(registry);
for (JSType alternate : results) {
builder.addAlternate(alternate);
}
return builder.build(); // maybe not a union
}
return type;
}
@Override
public JSType caseTemplateType(TemplateType type) {
return type;
}
private JSType getNativeType(JSTypeNative nativeType) {
return registry.getNativeType(nativeType);
}
private boolean isNativeFunctionType(FunctionType type) {
return type.isNativeObjectType();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Nick Santos
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.ErrorReporter;
/**
* A namespace type is a reference to a particular object.
*
* This is generally useful when you have a particular object
* in mind, but need to give it a name when it's passed to a function.
*
* For example,
* <code>
* /** @namespace /
* var jQuery = {};
*
* /** @return {jQuery.} /
* jQuery.get = function () {
* return jQuery // for easy chaining
* }
* </code>
*
* @see https://docs.google.com/document/d/1r37CJ6ZW0zk28IMn1Tu8UKKjs2WcJ-6dNEb3om7FoHQ
* @author nicholas.j.santos@gmail.com (Nick Santos)
*/
class NamespaceType extends NamedType {
private static final long serialVersionUID = 1L;
/**
* Create a namespace type based on the reference.
*/
NamespaceType(JSTypeRegistry registry, String reference,
String sourceName, int lineno, int charno) {
super(registry, reference, sourceName, lineno, charno);
}
/**
* Resolve the referenced type within the enclosing scope.
*/
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> enclosing) {
warning(t
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Sets;
import com.google.javascript.jscomp.ReferenceCollectingCallback.BasicBlock;
import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior;
import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference;
import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap;
import com.google.javascript.jscomp.Scope.Var;
import com.google.javascript.rhino.Node;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Checks variables to see if they are referenced before their declaration, or
* if they are redeclared in a way that is suspicious (i.e. not dictated by
* control structures). This is a more aggressive version of {@link VarCheck},
* but it lacks the cross-module checks.
*
* @author kushal@google.com (Kushal Dave)
*/
class VariableReferenceCheck implements HotSwapCompilerPass {
static final DiagnosticType UNDECLARED_REFERENCE = DiagnosticType.warning(
"JSC_REFERENCE_BEFORE_DECLARE",
"Variable referenced before declaration: {0}");
static final DiagnosticType REDECLARED_VARIABLE = DiagnosticType.warning(
"JSC_REDECLARED_VARIABLE",
"Redeclared variable: {0}");
static final DiagnosticType AMBIGUOUS_FUNCTION_DECL =
DiagnosticType.disabled("AMBIGUOUS_FUNCTION_DECL",
"Ambiguous use of a named function: {0}.");
private final AbstractCompiler compiler;
private final CheckLevel checkLevel;
// NOTE(nicksantos): It's a lot faster to use a shared Set that
// we clear after each method call, because the Set never gets too big.
private final Set<BasicBlock> blocksWithDeclarations = Sets.newHashSet();
public VariableReferenceCheck(AbstractCompiler compiler,
CheckLevel checkLevel) {
this.compiler = compiler;
this.checkLevel = checkLevel;
}
@Override
public void process(Node externs, Node root) {
ReferenceCollectingCallback callback = new ReferenceCollectingCallback(
compiler, new ReferenceCheckingBehavior());
callback.process(externs, root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
ReferenceCollectingCallback callback = new ReferenceCollectingCallback(
compiler, new ReferenceCheckingBehavior());
callback.hotSwapScript(scriptRoot, originalRoot);
}
/**
*
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> Behavior that checks variables for redeclaration or early references
* just after they go out of scope.
*/
private class ReferenceCheckingBehavior implements Behavior {
@Override
public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {
// TODO(bashir) In hot-swap version this means that for global scope we
// only go through all global variables accessed in the modified file not
// all global variables. This should be fixed.
// Check all vars after finishing a scope
for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) {
Var v = it.next();
checkVar(v, referenceMap.getReferences(v).references);
}
}
/**
* If the variable is declared more than once in a basic block, generate a
* warning. Also check if a variable is used in a given scope before it is
* declared, which suggest a likely error. Relies on the fact that
* references is in parse-tree order.
*/
private void checkVar(Var v, List<Reference> references) {
blocksWithDeclarations.clear();
boolean isDeclaredInScope = false;
boolean isUnhoistedNamedFunction = false;
Reference hoistedFn = null;
// Look for hoisted functions.
for (Reference reference : references) {
if (reference.isHoistedFunction()) {
blocksWithDeclarations.add(reference.getBasicBlock());
isDeclaredInScope = true;
hoistedFn = reference;
break;
} else if (NodeUtil.isFunctionDeclaration(
reference.getNode().getParent())) {
isUnhoistedNamedFunction = true;
}
}
for (Reference reference : references) {
if (reference == hoistedFn) {
continue;
}
BasicBlock basicBlock = reference.getBasicBlock();
boolean isDeclaration = reference.isDeclaration();
boolean allowDupe =
VarCheck.hasDuplicateDeclarationSuppression(
reference.getNode(), v);
if (isDeclaration && !allowDupe) {
// Look through all the declarations we've found so far, and
// check if any of them are before this block.
for (BasicBlock declaredBlock : blocksWithDeclarations) {
if (declaredBlock.provablyExecutesBefore(basicBlock)) {
// TODO(johnlenz): Fix AST generating clients that so they would
// have property StaticSourceFile attached at each node. Or
// better yet, make sure the generated code never violates
// the requirement to pass aggressive var check!
String filename = NodeUtil.getSourceName(reference.getNode());
compiler.report(
JSError.make(filename,
reference.getNode(),
checkLevel,
REDECLARED_VARIABLE, v.name));
break;
}
}
}
if (isUnhoistedNamedFunction && !isDeclaration && isDeclaredInScope) {
// Only allow an unhoisted named function to be used within the
// block it is declared.
for (BasicBlock declaredBlock : blocksWithDeclarations) {
if (!declaredBlock.provablyExecutesBefore(basicBlock)) {
String filename = NodeUtil.getSourceName(reference.getNode());
compiler.report(
JSError.make(filename,
reference.getNode(),
AMBIGUOUS_FUNCTION_DECL, v.name));
break;
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> final boolean nativeType;
// NOTE(nicksantos): The implicit prototype can change over time.
// Modeling this is a bear. Always call getImplicitPrototype(), because
// some subclasses override this to do special resolution handling.
private ObjectType implicitPrototypeFallback;
// If this is a function prototype, then this is the owner.
// A PrototypeObjectType can only be the prototype of one function. If we try
// to do this for multiple functions, then we'll have to create a new one.
private FunctionType ownerFunction = null;
// Whether the toString representation of this should be pretty-printed,
// by printing all properties.
private boolean prettyPrint = false;
private static final int MAX_PRETTY_PRINTED_PROPERTIES = 4;
/**
* Creates an object type.
*
* @param className the name of the class. May be {@code null} to
* denote an anonymous class.
*
* @param implicitPrototype the implicit prototype
* (a.k.a. {@code [[Prototype]]}) as defined by ECMA-262. If the
* implicit prototype is {@code null} the implicit prototype will be
* set to the {@link JSTypeNative#OBJECT_TYPE}.
*/
PrototypeObjectType(JSTypeRegistry registry, String className,
ObjectType implicitPrototype) {
this(registry, className, implicitPrototype, false, null);
}
/**
* Creates an object type, allowing specification of the implicit prototype,
* whether the object is native, and any templatized types.
*/
PrototypeObjectType(JSTypeRegistry registry, String className,
ObjectType implicitPrototype, boolean nativeType,
TemplateTypeMap templateTypeMap) {
super(registry, templateTypeMap);
this.properties = new PropertyMap();
this.properties.setParentSource(this);
this.className = className;
this.nativeType = nativeType;
if (nativeType || implicitPrototype != null) {
setImplicitPrototype(implicitPrototype);
} else {
setImplicitPrototype(
registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE));
}
}
@Override
PropertyMap getPropertyMap() {
return properties;
}
@Override
boolean defineProperty(String name, JSType type, boolean inferred,
Node propertyNode) {
if (hasOwnDeclaredProperty(name)) {
return false;
}
Property newProp = new Property(
name, type, inferred, propertyNode);
properties.putProperty(name, newProp);
return true;
}
@Override
public boolean removeProperty(String name) {
return properties.removeProperty(name);
}
@Override
public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) {
if (info != null) {
if (properties.getOwnProperty(propertyName) == null) {
// If docInfo was attached, but the type of the property
// was not defined anywhere, then we consider this an explicit
// declaration of the property.
defineInferredProperty(propertyName, getPropertyType(propertyName),
null);
}
// The prototype property is not represented as a normal Property.
// We probably don't want to attach any JSDoc to it anyway.
Property property = properties.getOwnProperty(propertyName);
if (property != null) {
property.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>setJSDocInfo(info);
}
}
}
@Override
public boolean matchesNumberContext() {
return isNumberObjectType() || isDateType() || isBooleanObjectType() ||
isStringObjectType() || hasOverridenNativeProperty("valueOf");
}
@Override
public boolean matchesStringContext() {
return isTheObjectType() || isStringObjectType() || isDateType() ||
isRegexpType() || isArrayType() || isNumberObjectType() ||
isBooleanObjectType() || hasOverridenNativeProperty("toString");
}
/**
* Given the name of a native object property, checks whether the property is
* present on the object and different from the native one.
*/
private boolean hasOverridenNativeProperty(String propertyName) {
if (isNativeObjectType()) {
return false;
}
JSType propertyType = getPropertyType(propertyName);
ObjectType nativeType =
isFunctionType() ?
registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE) :
registry.getNativeObjectType(JSTypeNative.OBJECT_PROTOTYPE);
JSType nativePropertyType = nativeType.getPropertyType(propertyName);
return propertyType != nativePropertyType;
}
@Override
public JSType unboxesTo() {
if (isStringObjectType()) {
return getNativeType(JSTypeNative.STRING_TYPE);
} else if (isBooleanObjectType()) {
return getNativeType(JSTypeNative.BOOLEAN_TYPE);
} else if (isNumberObjectType()) {
return getNativeType(JSTypeNative.NUMBER_TYPE);
} else {
return super.unboxesTo();
}
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
public boolean canBeCalled() {
return isRegexpType();
}
@Override
String toStringHelper(boolean forAnnotations) {
if (hasReferenceName()) {
return getReferenceName();
} else if (prettyPrint) {
// Don't pretty print recursively.
prettyPrint = false;
// Use a tree set so that the properties are sorted.
Set<String> propertyNames = Sets.newTreeSet();
for (ObjectType current = this;
current != null && !current.isNativeObjectType() &&
propertyNames.size() <= MAX_PRETTY_PRINTED_PROPERTIES;
current = current.getImplicitPrototype()) {
propertyNames.addAll(current.getOwnPropertyNames());
}
StringBuilder sb = new StringBuilder();
sb.append("{");
int i = 0;
for (String property : propertyNames) {
if (i > 0) {
sb.append(", ");
}
sb.append(property);
sb.append(": ");
sb.append(getPropertyType(property).toStringHelper(forAnnotations));
++i;
if (!forAnnotations && i == MAX_PRETTY_PRINTED_PROPERTIES) {
sb.append(", ...");
break;
}
}
sb.append("}");
prettyPrint = true;
return sb.toString();
} else {
return forAnnotations ? "?" : "{...}";
}
}
void setPrettyPrint(boolean prettyPrint) {
this.prettyPrint = prettyPrint;
}
boolean isPrettyPrint() {
return prettyPrint;
}
@Override
public FunctionType getConstructor() {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> return null;
}
@Override
public ObjectType getImplicitPrototype() {
return implicitPrototypeFallback;
}
/**
* This should only be reset on the FunctionPrototypeType, only to fix an
* incorrectly established prototype chain due to the user having a mismatch
* in super class declaration, and only before properties on that type are
* processed.
*/
final void setImplicitPrototype(ObjectType implicitPrototype) {
checkState(!hasCachedValues());
this.implicitPrototypeFallback = implicitPrototype;
}
@Override
public String getReferenceName() {
if (className != null) {
return className;
} else if (ownerFunction != null) {
return ownerFunction.getReferenceName() + ".prototype";
} else {
return null;
}
}
@Override
public boolean hasReferenceName() {
return className != null || ownerFunction != null;
}
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtypeHelper(this, that)) {
return true;
}
// Union types
if (that.isUnionType()) {
// The static {@code JSType.isSubtype} check already decomposed
// union types, so we don't need to check those again.
return false;
}
// record types
if (that.isRecordType()) {
return RecordType.isSubtype(this, that.toMaybeRecordType());
}
// Interfaces
// Find all the interfaces implemented by this class and compare each one
// to the interface instance.
ObjectType thatObj = that.toObjectType();
FunctionType thatCtor = thatObj == null ? null : thatObj.getConstructor();
if (getConstructor() != null && getConstructor().isInterface()) {
for (ObjectType thisInterface : getCtorExtendedInterfaces()) {
if (thisInterface.isSubtype(that)) {
return true;
}
}
} else if (thatCtor != null && thatCtor.isInterface()) {
Iterable<ObjectType> thisInterfaces = getCtorImplementedInterfaces();
for (ObjectType thisInterface : thisInterfaces) {
if (thisInterface.isSubtype(that)) {
return true;
}
}
}
// other prototype based objects
if (isUnknownType() || implicitPrototypeChainIsUnknown()) {
// If unsure, say 'yes', to avoid spurious warnings.
// TODO(user): resolve the prototype chain completely in all cases,
// to avoid guessing.
return true;
}
return thatObj != null && isImplicitPrototype(thatObj);
}
private boolean implicitPrototypeChainIsUnknown() {
ObjectType p = getImplicitPrototype();
while (p != null) {
if (p.isUnknownType()) {
return true;
}
p = p.getImplicitPrototype();
}
return false;
}
@Override
public boolean hasCachedValues() {
return super.hasCachedValues();
}
/** Whether this is a built-in object. */
@Override
public boolean isNativeObjectType() {
return nativeType;
}
@Override
void setOwnerFunction(FunctionType type) {
Preconditions.checkState(ownerFunction == null || type == null);
ownerFunction = type;
}
@Override
public FunctionType getOwnerFunction() {
return ownerFunction;
}
@Override
public Iterable<
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>ObjectType> getCtorImplementedInterfaces() {
return isFunctionPrototypeType()
? getOwnerFunction().getImplementedInterfaces()
: ImmutableList.<ObjectType>of();
}
@Override
public Iterable<ObjectType> getCtorExtendedInterfaces() {
return isFunctionPrototypeType()
? getOwnerFunction().getExtendedInterfaces()
: ImmutableList.<ObjectType>of();
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setResolvedTypeInternal(this);
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
implicitPrototypeFallback =
(ObjectType) implicitPrototype.resolve(t, scope);
}
for (Property prop : properties.values()) {
prop.setType(safeResolve(prop.getType(), t, scope));
}
return this;
}
@Override
public void matchConstraint(JSType constraint) {
// We only want to match constraints on anonymous types.
if (hasReferenceName()) {
return;
}
// Handle the case where the constraint object is a record type.
//
// param constraint {{prop: (number|undefined)}}
// function f(constraint) {}
// f({});
//
// We want to modify the object literal to match the constraint, by
// taking any each property on the record and trying to match
// properties on this object.
if (constraint.isRecordType()) {
matchRecordTypeConstraint(constraint.toObjectType());
} else if (constraint.isUnionType()) {
for (JSType alt : constraint.toMaybeUnionType().getAlternates()) {
if (alt.isRecordType()) {
matchRecordTypeConstraint(alt.toObjectType());
}
}
}
}
public void matchRecordTypeConstraint(ObjectType constraintObj) {
for (String prop : constraintObj.getOwnPropertyNames()) {
JSType propType = constraintObj.getPropertyType(prop);
if (!isPropertyTypeDeclared(prop)) {
JSType typeToInfer = propType;
if (!hasProperty(prop)) {
typeToInfer = getNativeType(JSTypeNative.VOID_TYPE)
.getLeastSupertype(propType);
}
defineInferredProperty(prop, typeToInfer, null);
}
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
/**
* Compiler (and the other classes in this package) does the following:
* <ul>
* <li>parses JS code
* <li>checks for undefined variables
* <li>performs optimizations such as constant folding and constants inlining
* <li>renames variables (to short names)
* <li>outputs compact JavaScript code
* </ul>
*
* External variables are declared in 'externs' files. For instance, the file
* may include definitions for global javascript/browser objects such as
* window, document.
*
*/
public class Compiler extends AbstractCompiler {
static final String SINGLETON_MODULE_NAME = "[singleton]";
static final DiagnosticType MODULE_DEPENDENCY_ERROR =
DiagnosticType.error("JSC_MODULE_DEPENDENCY_ERROR",
"Bad dependency: {0} -> {1}. "
+ "Modules must be listed in dependency order.");
static final DiagnosticType MISSING_ENTRY_ERROR = DiagnosticType.error(
"JSC_MISSING_ENTRY_ERROR",
"required entry point \"{0}\" never provided");
static final DiagnosticType MISSING_MODULE_ERROR = DiagnosticType.error(
"JSC_MISSING_ENTRY_ERROR",
"unknown module \"{0}\" specified in entry point spec");
// Used in PerformanceTracker
static final String PARSING_PASS_NAME = "parseInputs";
private static final String CONFIG_RESOURCE =
"com.google.javascript.jscomp.parsing.ParserConfig";
CompilerOptions options = null;
private PassConfig passes = null;
// The externs inputs
private List<CompilerInput> externs;
// The JS source modules
private List<JSModule> modules;
// The graph of the JS source modules. Must be null if there are less than
// 2 modules, because we use this as a signal for which passes to run.
private JSModuleGraph moduleGraph;
// The JS source inputs
private List<CompilerInput> inputs;
// error manager to which error management is delegated
private ErrorManager errorManager;
// Warnings guard for filtering warnings.
private WarningsGuard warningsGuard;
// Compile-time injected libraries. The node points to the last node of
// the library, so code can be inserted after.
private final Map<String, Node> injectedLibraries = Maps.newLinkedHashMap();
// Parse tree root nodes
Node externsRoot;
Node jsRoot;
Node externAndJsRoot;
private Map<InputId, CompilerInput> inputsById;
/** The source code map */
private SourceMap sourceMap;
/** The externs created from the exports. */
private String externExports = null;
/**
* Ids for function inlining so that each declared name remains
* unique.
*/
private int uniqueNameId = 0;
/**
* Whether to assume there are references to the RegExp Global object
* properties.
*/
private boolean hasRegExpGlobalReferences = true;
/** The function information map */
private FunctionInformationMap functionInformationMap;
/** Debugging information */
private final StringBuilder debugLog = new StringBuilder();
/** Detect
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>s Google-specific coding conventions. */
CodingConvention defaultCodingConvention = new ClosureCodingConvention();
private JSTypeRegistry typeRegistry;
private Config parserConfig = null;
private ReverseAbstractInterpreter abstractInterpreter;
private TypeValidator typeValidator;
// The compiler can ask phaseOptimizer for things like which pass is currently
// running, or which functions have been changed by optimizations
private PhaseOptimizer phaseOptimizer = null;
public PerformanceTracker tracker;
// The oldErrorReporter exists so we can get errors from the JSTypeRegistry.
private final com.google.javascript.rhino.ErrorReporter oldErrorReporter =
RhinoErrorReporter.forOldRhino(this);
// This error reporter gets the messages from the current Rhino parser.
private final ErrorReporter defaultErrorReporter =
RhinoErrorReporter.forNewRhino(this);
/** Error strings used for reporting JSErrors */
public static final DiagnosticType OPTIMIZE_LOOP_ERROR = DiagnosticType.error(
"JSC_OPTIMIZE_LOOP_ERROR",
"Exceeded max number of optimization iterations: {0}");
public static final DiagnosticType MOTION_ITERATIONS_ERROR =
DiagnosticType.error("JSC_OPTIMIZE_LOOP_ERROR",
"Exceeded max number of code motion iterations: {0}");
// We use many recursive algorithms that use O(d) memory in the depth
// of the tree.
private static final long COMPILER_STACK_SIZE = (1 << 21); // About 2MB
/**
* Under JRE 1.6, the JS Compiler overflows the stack when running on some
* large or complex JS code. When threads are available, we run all compile
* jobs on a separate thread with a larger stack.
*
* That way, we don't have to increase the stack size for *every* thread
* (which is what -Xss does).
*/
private static final ExecutorService compilerExecutor =
Executors.newCachedThreadPool(new ThreadFactory() {
@Override public Thread newThread(Runnable r) {
return new Thread(null, r, "jscompiler", COMPILER_STACK_SIZE);
}
});
/**
* Use a dedicated compiler thread per Compiler instance.
*/
private Thread compilerThread = null;
/** Whether to use threads. */
private boolean useThreads = true;
/**
* Logger for the whole com.google.javascript.jscomp domain -
* setting configuration for this logger affects all loggers
* in other classes within the compiler.
*/
private static final Logger logger =
Logger.getLogger("com.google.javascript.jscomp");
private final PrintStream outStream;
private GlobalVarReferenceMap globalRefMap = null;
private volatile double progress = 0.0;
private String lastPassName;
/**
* Creates a Compiler that reports errors and warnings to its logger.
*/
public Compiler() {
this((PrintStream) null);
}
/**
* Creates a Compiler that reports errors and warnings to an output stream.
*/
public Compiler(PrintStream stream) {
addChangeHandler(recentChange);
outStream = stream;
}
/**
* Creates a Compiler that uses a custom error manager.
*/
public Compiler(ErrorManager errorManager) {
this();
setErrorManager(errorManager);
}
/**
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>_LIST_ERROR",
"At least one module must be provided");
private static final DiagnosticType EMPTY_ROOT_MODULE_ERROR =
DiagnosticType.error("JSC_EMPTY_ROOT_MODULE_ERROR",
"Root module '{0}' must contain at least one source code input");
/**
* Verifies that at least one module has been provided and that the first one
* has at least one source code input.
*/
private void checkFirstModule(List<JSModule> modules) {
if (modules.isEmpty()) {
report(JSError.make(EMPTY_MODULE_LIST_ERROR));
} else if (modules.get(0).getInputs().isEmpty() && modules.size() > 1) {
// The root module may only be empty if there is exactly 1 module.
report(JSError.make(EMPTY_ROOT_MODULE_ERROR,
modules.get(0).getName()));
}
}
/**
* Empty modules get an empty "fill" file, so that we can move code into
* an empty module.
*/
static String createFillFileName(String moduleName) {
return "[" + moduleName + "]";
}
/**
* Fill any empty modules with a place holder file. It makes any cross module
* motion easier.
*/
private static void fillEmptyModules(List<JSModule> modules) {
for (JSModule module : modules) {
if (module.getInputs().isEmpty()) {
module.add(SourceFile.fromCode(
createFillFileName(module.getName()), ""));
}
}
}
/**
* Rebuilds the internal list of inputs by iterating over all modules.
* This is necessary if inputs have been added to or removed from a module
* after the {@link #init(List, List, CompilerOptions)} call.
*/
public void rebuildInputsFromModules() {
inputs = getAllInputsFromModules(modules);
initInputsByIdMap();
}
/**
* Builds a single list of all module inputs. Verifies that it contains no
* duplicates.
*/
private static List<CompilerInput> getAllInputsFromModules(
List<JSModule> modules) {
List<CompilerInput> inputs = Lists.newArrayList();
Map<String, JSModule> inputMap = Maps.newHashMap();
for (JSModule module : modules) {
for (CompilerInput input : module.getInputs()) {
String inputName = input.getName();
// NOTE(nicksantos): If an input is in more than one module,
// it will show up twice in the inputs list, and then we
// will get an error down the line.
inputs.add(input);
inputMap.put(inputName, module);
}
}
return inputs;
}
static final DiagnosticType DUPLICATE_INPUT =
DiagnosticType.error("JSC_DUPLICATE_INPUT", "Duplicate input: {0}");
static final DiagnosticType DUPLICATE_EXTERN_INPUT =
DiagnosticType.error("JSC_DUPLICATE_EXTERN_INPUT",
"Duplicate extern input: {0}");
/**
* Creates a map to make looking up an input by name fast. Also checks for
* duplicate inputs.
*/
void initInputsByIdMap() {
inputsById = new HashMap<InputId, CompilerInput>();
for (CompilerInput input : extern
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>acer t = newTracer("generateReport");
errorManager.generateReport();
stopTracer(t, "generateReport");
}
}
private Result compile() {
return runInCompilerThread(new Callable<Result>() {
@Override
public Result call() throws Exception {
compileInternal();
return getResult();
}
});
}
/**
* Disable threads. This is for clients that run on AppEngine and
* don't have threads.
*/
public void disableThreads() {
useThreads = false;
}
@SuppressWarnings("unchecked")
<T> T runInCompilerThread(final Callable<T> callable) {
T result = null;
final Throwable[] exception = new Throwable[1];
Preconditions.checkState(
compilerThread == null || compilerThread == Thread.currentThread(),
"Please do not share the Compiler across threads");
// If the compiler thread is available, use it.
if (useThreads && compilerThread == null) {
try {
final boolean dumpTraceReport =
options != null && options.tracer.isOn();
Callable<T> bootCompilerThread = new Callable<T>() {
@Override
public T call() {
try {
compilerThread = Thread.currentThread();
if (dumpTraceReport) {
Tracer.initCurrentThreadTrace();
}
return callable.call();
} catch (Throwable e) {
exception[0] = e;
} finally {
compilerThread = null;
if (dumpTraceReport) {
Tracer.logCurrentThreadTrace();
tracker.outputTracerReport(outStream == null ?
System.out : outStream);
}
Tracer.clearCurrentThreadTrace();
}
return null;
}
};
result = compilerExecutor.submit(bootCompilerThread).get();
} catch (InterruptedException e) {
throw Throwables.propagate(e);
} catch (ExecutionException e) {
throw Throwables.propagate(e);
}
} else {
try {
result = callable.call();
} catch (Exception e) {
exception[0] = e;
} finally {
Tracer.clearCurrentThreadTrace();
}
}
// Pass on any exception caught by the runnable object.
if (exception[0] != null) {
throw new RuntimeException(exception[0]);
}
return result;
}
private void compileInternal() {
setProgress(0.0, null);
parse();
// 15 percent of the work is assumed to be for parsing (based on some
// minimal analysis on big JS projects, of course this depends on options)
setProgress(0.15, "parse");
if (hasErrors()) {
return;
}
if (!precheck()) {
return;
}
if (options.nameAnonymousFunctionsOnly) {
// TODO(nicksantos): Move this into an instrument() phase maybe?
check();
return;
}
if (!options.skipAllPasses) {
check();
if (hasErrors()) {
return;
}
// IDE-mode is defined to stop here, before the heavy rewriting begins.
if (!options.ideMode) {
optimize();
}
}
if (options.recordFunctionInformation) {
recordFunctionInformation();
}
if (options.devMode == Dev
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>this);
process(pass);
externExports = pass.getGeneratedExterns();
endPass();
}
@Override
void process(CompilerPass p) {
p.process(externsRoot, jsRoot);
}
private final PassFactory sanityCheck =
new PassFactory("sanityCheck", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new SanityCheck(compiler);
}
};
private void maybeSanityCheck() {
if (options.devMode == DevMode.EVERY_PASS) {
runSanityCheck();
}
}
private void runSanityCheck() {
sanityCheck.create(this).process(externsRoot, jsRoot);
}
/**
* Removes try/catch/finally statements for easier debugging.
*/
void removeTryCatchFinally() {
logger.fine("Remove try/catch/finally");
startPass("removeTryCatchFinally");
RemoveTryCatch r = new RemoveTryCatch(this);
process(r);
endPass();
}
/**
* Strips code for smaller compiled code. This is useful for removing debug
* statements to prevent leaking them publicly.
*/
void stripCode(Set<String> stripTypes, Set<String> stripNameSuffixes,
Set<String> stripTypePrefixes, Set<String> stripNamePrefixes) {
logger.fine("Strip code");
startPass("stripCode");
StripCode r = new StripCode(this, stripTypes, stripNameSuffixes,
stripTypePrefixes, stripNamePrefixes);
if (options.getTweakProcessing().shouldStrip()) {
r.enableTweakStripping();
}
process(r);
endPass();
}
/**
* Runs custom passes that are designated to run at a particular time.
*/
private void runCustomPasses(CustomPassExecutionTime executionTime) {
if (options.customPasses != null) {
Tracer t = newTracer("runCustomPasses");
try {
for (CompilerPass p : options.customPasses.get(executionTime)) {
process(p);
}
} finally {
stopTracer(t, "runCustomPasses");
}
}
}
private Tracer currentTracer = null;
private String currentPassName = null;
/**
* Marks the beginning of a pass.
*/
void startPass(String passName) {
Preconditions.checkState(currentTracer == null);
currentPassName = passName;
currentTracer = newTracer(passName);
}
/**
* Marks the end of a pass.
*/
void endPass() {
Preconditions.checkState(currentTracer != null,
"Tracer should not be null at the end of a pass.");
stopTracer(currentTracer, currentPassName);
currentPassName = null;
currentTracer = null;
maybeSanityCheck();
}
/**
* Returns a new tracer for the given pass name.
*/
Tracer newTracer(String passName) {
String comment = passName
+ (recentChange.hasCodeChanged() ? " on recently changed AST" : "");
if (options.tracer.isOn()) {
tracker.recordPassStart(passName, true);
}
return new Tracer("Compiler", comment);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
}
void stopTracer(Tracer t, String passName) {
long result = t.stop();
if (options.tracer.isOn()) {
tracker.recordPassStop(passName, result);
}
}
/**
* Returns the result of the compilation.
*/
public Result getResult() {
PassConfig.State state = getPassConfig().getIntermediateState();
return new Result(getErrors(), getWarnings(), debugLog.toString(),
state.variableMap, state.propertyMap,
state.anonymousFunctionNameMap, state.stringMap, functionInformationMap,
sourceMap, externExports, state.cssNames, state.idGeneratorMap);
}
/**
* Returns an array constructed from errors + temporary warnings.
*/
public JSError[] getMessages() {
return getErrors();
}
/**
* Returns the array of errors (never null).
*/
public JSError[] getErrors() {
return errorManager.getErrors();
}
/**
* Returns the array of warnings (never null).
*/
public JSError[] getWarnings() {
return errorManager.getWarnings();
}
@Override
public Node getRoot() {
return externAndJsRoot;
}
/**
* Creates a new id for making unique names.
*/
private int nextUniqueNameId() {
return uniqueNameId++;
}
/**
* Resets the unique name id counter
*/
@VisibleForTesting
void resetUniqueNameId() {
uniqueNameId = 0;
}
@Override
Supplier<String> getUniqueNameIdSupplier() {
final Compiler self = this;
return new Supplier<String>() {
@Override
public String get() {
return String.valueOf(self.nextUniqueNameId());
}
};
}
@Override
boolean areNodesEqualForInlining(Node n1, Node n2) {
if (options.ambiguateProperties ||
options.disambiguateProperties) {
// The type based optimizations require that type information is preserved
// during other optimizations.
return n1.isEquivalentToTyped(n2);
} else {
return n1.isEquivalentTo(n2);
}
}
//------------------------------------------------------------------------
// Inputs
//------------------------------------------------------------------------
// TODO(nicksantos): Decide which parts of these belong in an AbstractCompiler
// interface, and which ones should always be injected.
@Override
public CompilerInput getInput(InputId id) {
return inputsById.get(id);
}
/**
* Removes an input file from AST.
* @param id The id of the input to be removed.
*/
protected void removeExternInput(InputId id) {
CompilerInput input = getInput(id);
if (input == null) {
return;
}
Preconditions.checkState(input.isExtern(), "Not an extern input: %s", input.getName());
inputsById.remove(id);
externs.remove(input);
Node root = input.getAstRoot(this);
if (root != null) {
root.detachFromParent();
}
}
@Override
public CompilerInput newExternInput(String name) {
SourceAst ast = new SyntheticAst(name);
if (inputsById.containsKey(ast.getInputId()))
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>newRoot);
CompilerInput newInput = new CompilerInput(ast);
// TODO(tylerg): handle this for multiple modules at some point.
if (moduleGraph == null && !modules.isEmpty()) {
// singleton module
modules.get(0).add(newInput);
}
putCompilerInput(ast.getInputId(), newInput);
return true;
}
@Override
JSModuleGraph getModuleGraph() {
return moduleGraph;
}
/**
* Gets a module graph. This will always return a module graph, even
* in the degenerate case when there's only one module.
*/
JSModuleGraph getDegenerateModuleGraph() {
return moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph;
}
@Override
public JSTypeRegistry getTypeRegistry() {
if (typeRegistry == null) {
typeRegistry = new JSTypeRegistry(oldErrorReporter, options.looseTypes);
}
return typeRegistry;
}
@Override
public MemoizedScopeCreator getTypedScopeCreator() {
return getPassConfig().getTypedScopeCreator();
}
@SuppressWarnings("unchecked")
DefaultPassConfig ensureDefaultPassConfig() {
PassConfig passes = getPassConfig().getBasePassConfig();
Preconditions.checkState(passes instanceof DefaultPassConfig,
"PassConfigs must eventually delegate to the DefaultPassConfig");
return (DefaultPassConfig) passes;
}
public SymbolTable buildKnownSymbolTable() {
SymbolTable symbolTable = new SymbolTable(getTypeRegistry());
MemoizedScopeCreator typedScopeCreator = getTypedScopeCreator();
if (typedScopeCreator != null) {
symbolTable.addScopes(typedScopeCreator.getAllMemoizedScopes());
symbolTable.addSymbolsFrom(typedScopeCreator);
} else {
symbolTable.findScopes(this, externsRoot, jsRoot);
}
GlobalNamespace globalNamespace =
ensureDefaultPassConfig().getGlobalNamespace();
if (globalNamespace != null) {
symbolTable.addSymbolsFrom(globalNamespace);
}
ReferenceCollectingCallback refCollector =
new ReferenceCollectingCallback(
this, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR);
NodeTraversal.traverse(this, getRoot(), refCollector);
symbolTable.addSymbolsFrom(refCollector);
PreprocessorSymbolTable preprocessorSymbolTable =
ensureDefaultPassConfig().getPreprocessorSymbolTable();
if (preprocessorSymbolTable != null) {
symbolTable.addSymbolsFrom(preprocessorSymbolTable);
}
symbolTable.fillNamespaceReferences();
symbolTable.fillPropertyScopes();
symbolTable.fillThisReferences(this, externsRoot, jsRoot);
symbolTable.fillPropertySymbols(this, externsRoot, jsRoot);
symbolTable.fillJSDocInfo(this, externsRoot, jsRoot);
return symbolTable;
}
@Override
public Scope getTopScope() {
return getPassConfig().getTopScope();
}
@Override
public ReverseAbstractInterpreter getReverseAbstractInterpreter() {
if (abstractInterpreter == null) {
ChainableReverseAbstractInterpreter interpreter =
new SemanticReverseAbstractInterpreter(
getCodingConvention(), getTypeRegistry());
if (options.closurePass) {
interpreter = new ClosureReverseAbstractInterpreter(
getCodingConvention(), getTypeRegistry())
.append(interpreter).getFirst();
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> abstractInterpreter = interpreter;
}
return abstractInterpreter;
}
@Override
TypeValidator getTypeValidator() {
if (typeValidator == null) {
typeValidator = new TypeValidator(this);
}
return typeValidator;
}
//------------------------------------------------------------------------
// Parsing
//------------------------------------------------------------------------
/**
* Parses the externs and main inputs.
*
* @return A synthetic root node whose two children are the externs root
* and the main root
*/
Node parseInputs() {
boolean devMode = options.devMode != DevMode.OFF;
// If old roots exist (we are parsing a second time), detach each of the
// individual file parse trees.
if (externsRoot != null) {
externsRoot.detachChildren();
}
if (jsRoot != null) {
jsRoot.detachChildren();
}
// Parse main JS sources.
jsRoot = IR.block();
jsRoot.setIsSyntheticBlock(true);
externsRoot = IR.block();
externsRoot.setIsSyntheticBlock(true);
externAndJsRoot = IR.block(externsRoot, jsRoot);
externAndJsRoot.setIsSyntheticBlock(true);
if (options.tracer.isOn()) {
tracker = new PerformanceTracker(jsRoot, options.tracer);
addChangeHandler(tracker.getCodeChangeHandler());
}
Tracer tracer = newTracer(PARSING_PASS_NAME);
try {
// Parse externs sources.
for (CompilerInput input : externs) {
Node n = input.getAstRoot(this);
if (hasErrors()) {
return null;
}
externsRoot.addChildToBack(n);
}
// Modules inferred in ProcessCommonJS pass.
if (options.transformAMDToCJSModules || options.processCommonJSModules) {
processAMDAndCommonJSModules();
}
hoistExterns(externsRoot);
// Check if the sources need to be re-ordered.
boolean staleInputs = false;
if (options.dependencyOptions.needsManagement()) {
for (CompilerInput input : inputs) {
// Forward-declare all the provided types, so that they
// are not flagged even if they are dropped from the process.
for (String provide : input.getProvides()) {
getTypeRegistry().forwardDeclareType(provide);
}
}
try {
inputs =
(moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph)
.manageDependencies(options.dependencyOptions, inputs);
staleInputs = true;
} catch (CircularDependencyException e) {
report(JSError.make(
JSModule.CIRCULAR_DEPENDENCY_ERROR, e.getMessage()));
} catch (MissingProvideException e) {
report(JSError.make(
MISSING_ENTRY_ERROR, e.getMessage()));
} catch (JSModuleGraph.MissingModuleException e) {
report(JSError.make(
MISSING_MODULE_ERROR, e.getMessage()));
}
// If in IDE mode, we ignore the error and keep going.
if (hasErrors()) {
return null;
}
}
hoistNoCompileFiles();
if (staleInputs) {
repartitionInputs();
}
// Build the AST.
for (CompilerInput
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> can
* process and creates JSModules and the corresponding dependency tree
* on the way.
*/
void processAMDAndCommonJSModules() {
Map<String, JSModule> modulesByName = Maps.newLinkedHashMap();
Map<CompilerInput, JSModule> modulesByInput = Maps.newLinkedHashMap();
// TODO(nicksantos): Refactor module dependency resolution to work nicely
// with multiple ways to express dependencies. Directly support JSModules
// that are equivalent to a signal file and which express their deps
// directly in the source.
for (CompilerInput input : inputs) {
input.setCompiler(this);
Node root = input.getAstRoot(this);
if (root == null) {
continue;
}
if (options.transformAMDToCJSModules) {
new TransformAMDToCJSModule(this).process(null, root);
}
if (options.processCommonJSModules) {
ProcessCommonJSModules cjs = new ProcessCommonJSModules(this,
options.commonJSModulePathPrefix);
cjs.process(null, root);
JSModule m = cjs.getModule();
if (m != null) {
modulesByName.put(m.getName(), m);
modulesByInput.put(input, m);
}
}
}
if (options.processCommonJSModules) {
List<JSModule> modules = Lists.newArrayList(modulesByName.values());
if (!modules.isEmpty()) {
this.modules = modules;
this.moduleGraph = new JSModuleGraph(this.modules);
}
for (JSModule module : modules) {
for (CompilerInput input : module.getInputs()) {
for (String require : input.getRequires()) {
JSModule dependency = modulesByName.get(require);
if (dependency == null) {
report(JSError.make(MISSING_ENTRY_ERROR, require));
} else {
module.addDependency(dependency);
}
}
}
}
try {
modules = Lists.newArrayList();
for (CompilerInput input : this.moduleGraph.manageDependencies(
options.dependencyOptions, inputs)) {
modules.add(modulesByInput.get(input));
}
JSModule root = new JSModule("root");
for (JSModule m : modules) {
m.addDependency(root);
}
modules.add(0, root);
SortedDependencies<JSModule> sorter =
new SortedDependencies<JSModule>(modules);
modules = sorter.getDependenciesOf(modules, true);
this.modules = modules;
this.moduleGraph = new JSModuleGraph(modules);
} catch (Exception e) {
Throwables.propagate(e);
}
}
}
public Node parse(SourceFile file) {
initCompilerOptionsIfTesting();
addToDebugLog("Parsing: " + file.getName());
return new JsAst(file).getAstRoot(this);
}
private int syntheticCodeId = 0;
@Override
Node parseSyntheticCode(String js) {
CompilerInput input = new CompilerInput(
SourceFile.fromCode(" [synthetic:" + (++syntheticCodeId) + "] ", js));
putCompilerInput(input.getInputId(), input);
return input.getAstRoot(this);
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> /**
* Allow subclasses to override the default CompileOptions object.
*/
protected CompilerOptions newCompilerOptions() {
return new CompilerOptions();
}
void initCompilerOptionsIfTesting() {
if (options == null) {
// initialization for tests that don't initialize the compiler
// by the normal mechanisms.
initOptions(newCompilerOptions());
}
}
@Override
Node parseSyntheticCode(String fileName, String js) {
initCompilerOptionsIfTesting();
return parse(SourceFile.fromCode(fileName, js));
}
@Override
Node parseTestCode(String js) {
initCompilerOptionsIfTesting();
CompilerInput input = new CompilerInput(
SourceFile.fromCode("[testcode]", js));
if (inputsById == null) {
inputsById = Maps.newHashMap();
}
putCompilerInput(input.getInputId(), input);
return input.getAstRoot(this);
}
@Override
ErrorReporter getDefaultErrorReporter() {
return defaultErrorReporter;
}
//------------------------------------------------------------------------
// Convert back to source code
//------------------------------------------------------------------------
/**
* Converts the main parse tree back to JS code.
*/
public String toSource() {
return runInCompilerThread(new Callable<String>() {
@Override
public String call() throws Exception {
Tracer tracer = newTracer("toSource");
try {
CodeBuilder cb = new CodeBuilder();
if (jsRoot != null) {
int i = 0;
for (Node scriptNode = jsRoot.getFirstChild();
scriptNode != null;
scriptNode = scriptNode.getNext()) {
toSource(cb, i++, scriptNode);
}
}
return cb.toString();
} finally {
stopTracer(tracer, "toSource");
}
}
});
}
/**
* Converts the parse tree for each input back to JS code.
*/
public String[] toSourceArray() {
return runInCompilerThread(new Callable<String[]>() {
@Override
public String[] call() throws Exception {
Tracer tracer = newTracer("toSourceArray");
try {
int numInputs = inputs.size();
String[] sources = new String[numInputs];
CodeBuilder cb = new CodeBuilder();
for (int i = 0; i < numInputs; i++) {
Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
cb.reset();
toSource(cb, i, scriptNode);
sources[i] = cb.toString();
}
return sources;
} finally {
stopTracer(tracer, "toSourceArray");
}
}
});
}
/**
* Converts the parse tree for a module back to JS code.
*/
public String toSource(final JSModule module) {
return runInCompilerThread(new Callable<String>() {
@Override
public String call() throws Exception {
List<CompilerInput> inputs = module.getInputs();
int numInputs = inputs.size();
if (numInputs == 0) {
return "";
}
CodeBuilder cb = new CodeBuilder();
for (int i = 0; i < numInputs; i++) {
Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
if (scriptNode == null) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
throw new IllegalArgumentException(
"Bad module: " + module.getName());
}
toSource(cb, i, scriptNode);
}
return cb.toString();
}
});
}
/**
* Converts the parse tree for each input in a module back to JS code.
*/
public String[] toSourceArray(final JSModule module) {
return runInCompilerThread(new Callable<String[]>() {
@Override
public String[] call() throws Exception {
List<CompilerInput> inputs = module.getInputs();
int numInputs = inputs.size();
if (numInputs == 0) {
return new String[0];
}
String[] sources = new String[numInputs];
CodeBuilder cb = new CodeBuilder();
for (int i = 0; i < numInputs; i++) {
Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
if (scriptNode == null) {
throw new IllegalArgumentException(
"Bad module input: " + inputs.get(i).getName());
}
cb.reset();
toSource(cb, i, scriptNode);
sources[i] = cb.toString();
}
return sources;
}
});
}
/**
* Writes out JS code from a root node. If printing input delimiters, this
* method will attach a comment to the start of the text indicating which
* input the output derived from. If there were any preserve annotations
* within the root's source, they will also be printed in a block comment
* at the beginning of the output.
*/
public void toSource(final CodeBuilder cb,
final int inputSeqNum,
final Node root) {
runInCompilerThread(new Callable<Void>() {
@Override
public Void call() throws Exception {
if (options.printInputDelimiter) {
if ((cb.getLength() > 0) && !cb.endsWith("\n")) {
cb.append("\n"); // Make sure that the label starts on a new line
}
Preconditions.checkState(root.isScript());
String delimiter = options.inputDelimiter;
String inputName = root.getInputId().getIdName();
String sourceName = root.getSourceFileName();
Preconditions.checkState(sourceName != null);
Preconditions.checkState(!sourceName.isEmpty());
delimiter = delimiter
.replaceAll("%name%", Matcher.quoteReplacement(inputName))
.replaceAll("%num%", String.valueOf(inputSeqNum));
cb.append(delimiter)
.append("\n");
}
if (root.getJSDocInfo() != null &&
root.getJSDocInfo().getLicense() != null) {
cb.append("/*\n")
.append(root.getJSDocInfo().getLicense())
.append("*/\n");
}
// If there is a valid source map, then indicate to it that the current
// root node's mappings are offset by the given string builder buffer.
if (options.sourceMapOutputPath != null) {
sourceMap.setStartingPosition(
cb.getLineIndex(), cb.getColumnIndex());
}
// if LanguageMode is ECMASCRIPT5_STRICT, only print 'use strict'
// for the first input file
String code = toSource(root, sourceMap, inputSeq
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Num == 0);
if (!code.isEmpty()) {
cb.append(code);
// In order to avoid parse ambiguity when files are concatenated
// together, all files should end in a semi-colon. Do a quick
// heuristic check if there's an obvious semi-colon already there.
int length = code.length();
char lastChar = code.charAt(length - 1);
char secondLastChar = length >= 2 ?
code.charAt(length - 2) : '\0';
boolean hasSemiColon = lastChar == ';' ||
(lastChar == '\n' && secondLastChar == ';');
if (!hasSemiColon) {
cb.append(";");
}
}
return null;
}
});
}
/**
* Generates JavaScript source code for an AST, doesn't generate source
* map info.
*/
@Override
String toSource(Node n) {
initCompilerOptionsIfTesting();
return toSource(n, null, true);
}
/**
* Generates JavaScript source code for an AST.
*/
private String toSource(Node n, SourceMap sourceMap, boolean firstOutput) {
CodePrinter.Builder builder = new CodePrinter.Builder(n);
builder.setCompilerOptions(options);
builder.setSourceMap(sourceMap);
builder.setTagAsStrict(firstOutput &&
options.getLanguageOut() == LanguageMode.ECMASCRIPT5_STRICT);
return builder.build();
}
/**
* Stores a buffer of text to which more can be appended. This is just like a
* StringBuilder except that we also track the number of lines.
*/
public static class CodeBuilder {
private final StringBuilder sb = new StringBuilder();
private int lineCount = 0;
private int colCount = 0;
/** Removes all text, but leaves the line count unchanged. */
void reset() {
sb.setLength(0);
}
/** Appends the given string to the text buffer. */
CodeBuilder append(String str) {
sb.append(str);
// Adjust the line and column information for the new text.
int index = -1;
int lastIndex = index;
while ((index = str.indexOf('\n', index + 1)) >= 0) {
++lineCount;
lastIndex = index;
}
if (lastIndex == -1) {
// No new lines, append the new characters added.
colCount += str.length();
} else {
colCount = str.length() - (lastIndex + 1);
}
return this;
}
/** Returns all text in the text buffer. */
@Override
public String toString() {
return sb.toString();
}
/** Returns the length of the text buffer. */
public int getLength() {
return sb.length();
}
/** Returns the (zero-based) index of the last line in the text buffer. */
int getLineIndex() {
return lineCount;
}
/** Returns the (zero-based) index of the last column in the text buffer. */
int getColumnIndex() {
return colCount;
}
/** Determines whether the text ends with the given suffix. */
boolean endsWith(String suffix) {
return (sb.length()
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> > suffix.length())
&& suffix.equals(sb.substring(sb.length() - suffix.length()));
}
}
//------------------------------------------------------------------------
// Optimizations
//------------------------------------------------------------------------
public void optimize() {
// Ideally, this pass should be the first pass run, however:
// 1) VariableReferenceCheck reports unexpected warnings if Normalize
// is done first.
// 2) ReplaceMessages, stripCode, and potentially custom passes rely on
// unmodified local names.
normalize();
// Create extern exports after the normalize because externExports depends on unique names.
if (options.isExternExportsEnabled()
|| options.externExportsPath != null) {
externExports();
}
phaseOptimizer = new PhaseOptimizer(this, tracker, null);
if (options.devMode == DevMode.EVERY_PASS) {
phaseOptimizer.setSanityCheck(sanityCheck);
}
if (options.getCheckDeterminism()) {
phaseOptimizer.setPrintAstHashcodes(true);
}
phaseOptimizer.consume(getPassConfig().getOptimizations());
phaseOptimizer.process(externsRoot, jsRoot);
phaseOptimizer = null;
}
@Override
void setCssRenamingMap(CssRenamingMap map) {
options.cssRenamingMap = map;
}
@Override
CssRenamingMap getCssRenamingMap() {
return options.cssRenamingMap;
}
/**
* Reprocesses the current defines over the AST. This is used by GwtCompiler
* to generate N outputs for different targets from the same (checked) AST.
* For each target, we apply the target-specific defines by calling
* {@code processDefines} and then {@code optimize} to optimize the AST
* specifically for that target.
*/
public void processDefines() {
(new DefaultPassConfig(options)).processDefines.create(this)
.process(externsRoot, jsRoot);
}
boolean isInliningForbidden() {
return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC ||
options.propertyRenaming ==
PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC;
}
/** Control Flow Analysis. */
ControlFlowGraph<Node> computeCFG() {
logger.fine("Computing Control Flow Graph");
Tracer tracer = newTracer("computeCFG");
ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false);
process(cfa);
stopTracer(tracer, "computeCFG");
return cfa.getCfg();
}
public void normalize() {
logger.fine("Normalizing");
startPass("normalize");
process(new Normalize(this, false));
endPass();
}
@Override
void prepareAst(Node root) {
CompilerPass pass = new PrepareAst(this);
pass.process(null, root);
}
void recordFunctionInformation() {
logger.fine("Recording function information");
startPass("recordFunctionInformation");
RecordFunctionInformation recordFunctionInfoPass =
new RecordFunctionInformation(
this, getPassConfig().getIntermediateState().functionNames);
process(recordFunctionInfoPass);
functionInformationMap = recordFunctionInfoPass.getMap();
endPass();
}
protected final RecentChange recentChange = new RecentChange();
private final List<CodeChangeHandler
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>> codeChangeHandlers =
Lists.<CodeChangeHandler>newArrayList();
/** Name of the synthetic input that holds synthesized externs. */
static final String SYNTHETIC_EXTERNS = "{SyntheticVarsDeclar}";
private CompilerInput synthesizedExternsInput = null;
@Override
void addChangeHandler(CodeChangeHandler handler) {
codeChangeHandlers.add(handler);
}
@Override
void removeChangeHandler(CodeChangeHandler handler) {
codeChangeHandlers.remove(handler);
}
@Override
void setScope(Node n) {
if (phaseOptimizer != null) {
phaseOptimizer.setScope(n);
}
}
@Override
Node getJsRoot() {
return jsRoot;
}
@Override
boolean hasScopeChanged(Node n) {
if (!analyzeChangedScopesOnly || phaseOptimizer == null) {
return true;
}
return phaseOptimizer.hasScopeChanged(n);
}
@Override
void reportChangeToEnclosingScope(Node n) {
if (phaseOptimizer != null) {
phaseOptimizer.reportChangeToEnclosingScope(n);
phaseOptimizer.startCrossScopeReporting();
reportCodeChange();
phaseOptimizer.endCrossScopeReporting();
} else {
reportCodeChange();
}
}
/**
* Some tests don't want to call the compiler "wholesale," they may not want
* to call check and/or optimize. With this method, tests can execute custom
* optimization loops.
*/
@VisibleForTesting
void setPhaseOptimizer(PhaseOptimizer po) {
this.phaseOptimizer = po;
}
@Override
public void reportCodeChange() {
for (CodeChangeHandler handler : codeChangeHandlers) {
handler.reportChange();
}
}
@Override
public CodingConvention getCodingConvention() {
CodingConvention convention = options.getCodingConvention();
convention = convention != null ? convention : defaultCodingConvention;
return convention;
}
@Override
public boolean isIdeMode() {
return options.ideMode;
}
@Override
public boolean acceptEcmaScript5() {
switch (options.getLanguageIn()) {
case ECMASCRIPT5:
case ECMASCRIPT5_STRICT:
return true;
case ECMASCRIPT3:
return false;
}
throw new IllegalStateException("unexpected language mode");
}
public LanguageMode languageMode() {
return options.getLanguageIn();
}
@Override
public boolean acceptConstKeyword() {
return options.acceptConstKeyword;
}
@Override
Config getParserConfig() {
if (parserConfig == null) {
Config.LanguageMode mode;
switch (options.getLanguageIn()) {
case ECMASCRIPT3:
mode = Config.LanguageMode.ECMASCRIPT3;
break;
case ECMASCRIPT5:
mode = Config.LanguageMode.ECMASCRIPT5;
break;
case ECMASCRIPT5_STRICT:
mode = Config.LanguageMode.ECMASCRIPT5_STRICT;
break;
default:
throw new IllegalStateException("unexpected language mode");
}
parserConfig = ParserRunner.createConfig(
isIdeMode(),
mode,
acceptConstKeyword(),
options.extraAnnotationNames);
}
return parserConfig
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>;
}
@Override
public boolean isTypeCheckingEnabled() {
return options.checkTypes;
}
//------------------------------------------------------------------------
// Error reporting
//------------------------------------------------------------------------
/**
* The warning classes that are available from the command-line, and
* are suppressible by the {@code @suppress} annotation.
*/
protected DiagnosticGroups getDiagnosticGroups() {
return new DiagnosticGroups();
}
@Override
public void report(JSError error) {
CheckLevel level = error.getDefaultLevel();
if (warningsGuard != null) {
CheckLevel newLevel = warningsGuard.level(error);
if (newLevel != null) {
level = newLevel;
}
}
if (level.isOn()) {
if (getOptions().errorHandler != null) {
getOptions().errorHandler.report(level, error);
}
errorManager.report(level, error);
}
}
@Override
public CheckLevel getErrorLevel(JSError error) {
Preconditions.checkNotNull(options);
return warningsGuard.level(error);
}
/**
* Report an internal error.
*/
@Override
void throwInternalError(String message, Exception cause) {
String finalMessage =
"INTERNAL COMPILER ERROR.\n" +
"Please report this problem.\n" + message;
RuntimeException e = new RuntimeException(finalMessage, cause);
if (cause != null) {
e.setStackTrace(cause.getStackTrace());
}
throw e;
}
/**
* Gets the number of errors.
*/
public int getErrorCount() {
return errorManager.getErrorCount();
}
/**
* Gets the number of warnings.
*/
public int getWarningCount() {
return errorManager.getWarningCount();
}
@Override
boolean hasHaltingErrors() {
return !isIdeMode() && getErrorCount() > 0;
}
/**
* Consults the {@link ErrorManager} to see if we've encountered errors
* that should halt compilation. <p>
*
* If {@link CompilerOptions#ideMode} is {@code true}, this function
* always returns {@code false} without consulting the error manager. The
* error manager will continue to be told about new errors and warnings, but
* the compiler will complete compilation of all inputs.<p>
*/
public boolean hasErrors() {
return hasHaltingErrors();
}
/** Called from the compiler passes, adds debug info */
@Override
void addToDebugLog(String str) {
debugLog.append(str);
debugLog.append('\n');
logger.fine(str);
}
@Override
SourceFile getSourceFileByName(String sourceName) {
// Here we assume that the source name is the input name, this
// is try of JavaScript parsed from source.
if (sourceName != null) {
CompilerInput input = inputsById.get(new InputId(sourceName));
if (input != null) {
return input.getSourceFile();
}
}
return null;
}
@Override
public String getSourceLine(String sourceName, int lineNumber) {
if (lineNumber < 1) {
return null;
}
SourceFile input = getSourceFileByName(sourceName);
if (input != null) {
return input.getLine
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>(lineNumber);
}
return null;
}
@Override
public Region getSourceRegion(String sourceName, int lineNumber) {
if (lineNumber < 1) {
return null;
}
SourceFile input = getSourceFileByName(sourceName);
if (input != null) {
return input.getRegion(lineNumber);
}
return null;
}
//------------------------------------------------------------------------
// Package-private helpers
//------------------------------------------------------------------------
@Override
Node getNodeForCodeInsertion(JSModule module) {
if (module == null) {
if (inputs.isEmpty()) {
throw new IllegalStateException("No inputs");
}
return inputs.get(0).getAstRoot(this);
}
List<CompilerInput> moduleInputs = module.getInputs();
if (moduleInputs.size() > 0) {
return moduleInputs.get(0).getAstRoot(this);
}
throw new IllegalStateException("Root module has no inputs");
}
public SourceMap getSourceMap() {
return sourceMap;
}
VariableMap getVariableMap() {
return getPassConfig().getIntermediateState().variableMap;
}
VariableMap getPropertyMap() {
return getPassConfig().getIntermediateState().propertyMap;
}
CompilerOptions getOptions() {
return options;
}
FunctionInformationMap getFunctionalInformationMap() {
return functionInformationMap;
}
/**
* Sets the logging level for the com.google.javascript.jscomp package.
*/
public static void setLoggingLevel(Level level) {
logger.setLevel(level);
}
/** Gets the DOT graph of the AST generated at the end of compilation. */
public String getAstDotGraph() throws IOException {
if (jsRoot != null) {
ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false);
cfa.process(null, jsRoot);
return DotFormatter.toDot(jsRoot, cfa.getCfg());
} else {
return "";
}
}
@Override
public ErrorManager getErrorManager() {
if (options == null) {
initOptions(newCompilerOptions());
}
return errorManager;
}
@Override
List<CompilerInput> getInputsInOrder() {
return Collections.<CompilerInput>unmodifiableList(inputs);
}
/**
* Returns an unmodifiable view of the compiler inputs indexed by id.
*/
public Map<InputId, CompilerInput> getInputsById() {
return Collections.unmodifiableMap(inputsById);
}
/**
* Gets the externs in the order in which they are being processed.
*/
List<CompilerInput> getExternsInOrder() {
return Collections.<CompilerInput>unmodifiableList(externs);
}
/**
* Stores the internal compiler state just before optimization is performed.
* This can be saved and restored in order to efficiently optimize multiple
* different output targets without having to perform checking multiple times.
*
* NOTE: This does not include all parts of the compiler's internal state. In
* particular, SourceFiles and CompilerOptions are not recorded. In
* order to recreate a Compiler instance from scratch, you would need to
* call {@code init} with the same arguments as in the initial creation before
* restoring intermediate state.
*/
public static class IntermediateState implements Serializable
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> {
private static final long serialVersionUID = 1L;
Node externsRoot;
private Node jsRoot;
private List<CompilerInput> externs;
private List<CompilerInput> inputs;
private List<JSModule> modules;
private PassConfig.State passConfigState;
private JSTypeRegistry typeRegistry;
private AbstractCompiler.LifeCycleStage lifeCycleStage;
private Map<String, Node> injectedLibraries;
private IntermediateState() {}
}
/**
* Returns the current internal state, excluding the input files and modules.
*/
public IntermediateState getState() {
IntermediateState state = new IntermediateState();
state.externsRoot = externsRoot;
state.jsRoot = jsRoot;
state.externs = externs;
state.inputs = inputs;
state.modules = modules;
state.passConfigState = getPassConfig().getIntermediateState();
state.typeRegistry = typeRegistry;
state.lifeCycleStage = getLifeCycleStage();
state.injectedLibraries = Maps.newLinkedHashMap(injectedLibraries);
return state;
}
/**
* Sets the internal state to the capture given. Note that this assumes that
* the input files are already set up.
*/
public void setState(IntermediateState state) {
externsRoot = state.externsRoot;
jsRoot = state.jsRoot;
externs = state.externs;
inputs = state.inputs;
modules = state.modules;
passes = createPassConfigInternal();
getPassConfig().setIntermediateState(state.passConfigState);
typeRegistry = state.typeRegistry;
setLifeCycleStage(state.lifeCycleStage);
injectedLibraries.clear();
injectedLibraries.putAll(state.injectedLibraries);
}
@VisibleForTesting
List<CompilerInput> getInputsForTesting() {
return inputs;
}
@VisibleForTesting
List<CompilerInput> getExternsForTesting() {
return externs;
}
@Override
boolean hasRegExpGlobalReferences() {
return hasRegExpGlobalReferences;
}
@Override
void setHasRegExpGlobalReferences(boolean references) {
hasRegExpGlobalReferences = references;
}
@Override
void updateGlobalVarReferences(Map<Var, ReferenceCollection> refMapPatch,
Node collectionRoot) {
Preconditions.checkState(collectionRoot.isScript()
|| collectionRoot.isBlock());
if (globalRefMap == null) {
globalRefMap = new GlobalVarReferenceMap(getInputsInOrder(),
getExternsInOrder());
}
globalRefMap.updateGlobalVarReferences(refMapPatch, collectionRoot);
}
@Override
GlobalVarReferenceMap getGlobalVarReferences() {
return globalRefMap;
}
@Override
CompilerInput getSynthesizedExternsInput() {
if (synthesizedExternsInput == null) {
synthesizedExternsInput = newExternInput(SYNTHETIC_EXTERNS);
}
return synthesizedExternsInput;
}
@Override
public double getProgress() {
return progress;
}
@Override
String getLastPassName() {
return lastPassName;
}
@Override
void setProgress(double newProgress, String passName) {
this.lastPassName = passName
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>;
if (newProgress > 1.0) {
progress = 1.0;
} else {
progress = newProgress;
}
}
/**
* Replaces one file in a hot-swap mode. The given JsAst should be made
* from a new version of a file that already was present in the last compile
* call. If the file is new, this will silently ignored.
*
* @param ast the ast of the file that is being replaced
*/
public void replaceScript(JsAst ast) {
CompilerInput input = this.getInput(ast.getInputId());
if (!replaceIncrementalSourceAst(ast)) {
return;
}
Node originalRoot = input.getAstRoot(this);
processNewScript(ast, originalRoot);
}
/**
* Adds a new Script AST to the compile state. If a script for the same file
* already exists the script will not be added, instead a call to
* #replaceScript should be used.
*
* @param ast the ast of the new file
*/
public void addNewScript(JsAst ast) {
if (!addNewSourceAst(ast)) {
return;
}
Node emptyScript = new Node(Token.SCRIPT);
InputId inputId = ast.getInputId();
emptyScript.setInputId(inputId);
emptyScript.setStaticSourceFile(
SourceFile.fromCode(inputId.getIdName(), ""));
processNewScript(ast, emptyScript);
}
private void processNewScript(JsAst ast, Node originalRoot) {
Node js = ast.getAstRoot(this);
Preconditions.checkNotNull(js);
runHotSwap(originalRoot, js, this.getCleanupPassConfig());
// NOTE: If hot swap passes that use GlobalNamespace are added, we will need
// to revisit this approach to clearing GlobalNamespaces
runHotSwapPass(null, null, ensureDefaultPassConfig().garbageCollectChecks);
this.getTypeRegistry().clearNamedTypes();
this.removeSyntheticVarsInput();
runHotSwap(originalRoot, js, this.ensureDefaultPassConfig());
}
/**
* Execute the passes from a PassConfig instance over a single replaced file.
*/
private void runHotSwap(
Node originalRoot, Node js, PassConfig passConfig) {
for (PassFactory passFactory : passConfig.getChecks()) {
runHotSwapPass(originalRoot, js, passFactory);
}
}
private void runHotSwapPass(
Node originalRoot, Node js, PassFactory passFactory) {
HotSwapCompilerPass pass = passFactory.getHotSwapPass(this);
if (pass != null) {
logger.info("Performing HotSwap for pass " + passFactory.getName());
pass.hotSwapScript(js, originalRoot);
}
}
private PassConfig getCleanupPassConfig() {
return new CleanupPasses(getOptions());
}
private void removeSyntheticVarsInput() {
String sourceName = Compiler.SYNTHETIC_EXTERNS;
removeExternInput(new InputId(sourceName));
}
@Override
Node ensureLibraryInjected(String resourceName) {
if (injectedLibraries.containsKey(resourceName)) {
return null;
}
// All libraries depend on js/base.js
boolean
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> isBase = "base".equals(resourceName);
if (!isBase) {
ensureLibraryInjected("base");
}
Node firstChild = loadLibraryCode(resourceName).removeChildren();
Node lastChild = firstChild.getLastSibling();
Node parent = getNodeForCodeInsertion(null);
if (isBase) {
parent.addChildrenToFront(firstChild);
} else {
parent.addChildrenAfter(
firstChild, injectedLibraries.get("base"));
}
reportCodeChange();
injectedLibraries.put(resourceName, lastChild);
return lastChild;
}
/** Load a library as a resource */
@VisibleForTesting
Node loadLibraryCode(String resourceName) {
String originalCode;
try {
originalCode = CharStreams.toString(new InputStreamReader(
Compiler.class.getResourceAsStream(
String.format("js/%s.js", resourceName)),
Charsets.UTF_8));
} catch (IOException e) {
throw new RuntimeException(e);
}
return Normalize.parseAndNormalizeSyntheticCode(
this, originalCode,
String.format("jscomp_%s_", resourceName));
}
/** Returns the compiler version baked into the jar. */
public static String getReleaseVersion() {
ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE);
return config.getString("compiler.version");
}
/** Returns the compiler date baked into the jar. */
public static String getReleaseDate() {
ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE);
return config.getString("compiler.date");
}
/**
* {@inheritDoc}
*/
@Override
public void setOldParseTree(String sourceName, AstRoot oldAst) {
}
/**
* {@inheritDoc}
*/
@Override
public AstRoot getOldParseTreeByName(String sourceName) {
return null;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.parsing.ParserRunner;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import java.io.IOException;
import java.util.logging.Logger;
/**
* Generates an AST for a JavaScript source file.
*
*/
public class JsAst implements SourceAst {
private static final Logger logger_ = Logger.getLogger(JsAst.class.getName());
private static final long serialVersionUID = 1L;
private transient InputId inputId;
private transient SourceFile sourceFile;
private String fileName;
private Node root;
public JsAst(SourceFile sourceFile) {
this.inputId = new InputId(sourceFile.getName());
this.sourceFile = sourceFile;
this.fileName = sourceFile.getName();
}
@Override
public Node getAstRoot(AbstractCompiler compiler) {
if (root == null) {
parse(compiler);
root.setInputId(inputId);
}
return root;
}
@Override
public void clearAst() {
root = null;
// While we're at it, clear out any saved text in the source file on
// the assumption that if we're dumping the parse tree, then we probably
// assume regenerating everything else is a smart idea also.
sourceFile.clearCachedSource();
}
@Override
public InputId getInputId() {
return inputId;
}
@Override
public SourceFile getSourceFile() {
return sourceFile;
}
@Override
public void setSourceFile(SourceFile file) {
Preconditions.checkState(fileName.equals(file.getName()));
sourceFile = file;
}
private void parse(AbstractCompiler compiler) {
int startErrorCount = compiler.getErrorManager().getErrorCount();
try {
ParserRunner.ParseResult result = ParserRunner.parse(sourceFile, sourceFile.getCode(),
compiler.getParserConfig(),
compiler.getDefaultErrorReporter(),
logger_);
root = result.ast;
compiler.setOldParseTree(sourceFile.getName(), result.oldAst);
} catch (IOException e) {
compiler.report(
JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName()));
}
if (root == null ||
// Most passes try to report as many errors as possible,
// so there may already be errors. We only care if there were
// errors in the code we just
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
/**
* A source excerpt provider is responsible for building source code excerpt
* of specific locations, such as a specific line or a region around a
* given line number.
*
*/
public interface SourceExcerptProvider {
/**
* Source excerpt variety.
*/
enum SourceExcerpt {
/**
* Line excerpt.
*/
LINE {
@Override
public String get(SourceExcerptProvider source, String sourceName,
int lineNumber, ExcerptFormatter formatter) {
return formatter.formatLine(
source.getSourceLine(sourceName, lineNumber), lineNumber);
}
},
/**
* Region excerpt.
*/
REGION {
@Override
public String get(SourceExcerptProvider source, String sourceName,
int lineNumber, ExcerptFormatter formatter) {
return formatter.formatRegion(
source.getSourceRegion(sourceName, lineNumber));
}
};
/**
* Get a source excerpt string based on the type of the source excerpt.
*/
public abstract String get(SourceExcerptProvider source, String sourceName,
int lineNumber, ExcerptFormatter formatter);
}
/**
* Get the line indicated by the line number. This call will return only the
* specific line.
*
* @param lineNumber the line number, 1 being the first line of the file
* @return the line indicated, or {@code null} if it does not exist
*/
String getSourceLine(String sourceName, int lineNumber);
/**
* Get a region around the indicated line number. The exact definition of a
* region is implementation specific, but it must contain the line indicated
* by the line number. A region must not start or end by a carriage return.
*
* @param lineNumber the line number, 1 being the first line of the file
* @return the region around the line number indicated, or <code>null</null>
* if it does not exist
*/
Region getSourceRegion(String sourceName, int lineNumber);
/**
* A excerpt formatter is responsible of formatting source excerpts.
*/
interface ExcerptFormatter {
/**
* Format a line excerpt.
*/
String formatLine(String line, int lineNumber);
/**
* Format a region excerpt.
*/
String formatRegion(Region region);
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> assigns other types of values to this property,
* the property will take on the union of all these types.
*
* UNKNOWN properties are properties on the UNKNOWN type. The UNKNOWN
* type has all properties, but we do not know whether they are
* declared or inferred.
*
*/
public abstract class ObjectType extends JSType implements StaticScope<JSType> {
private boolean visited;
private JSDocInfo docInfo = null;
private boolean unknown = true;
ObjectType(JSTypeRegistry registry) {
super(registry);
}
ObjectType(JSTypeRegistry registry, TemplateTypeMap templateTypeMap) {
super(registry, templateTypeMap);
}
@Override
public Node getRootNode() { return null; }
@Override
public ObjectType getParentScope() {
return getImplicitPrototype();
}
/**
* Returns the property map that manages the set of properties for an object.
*/
PropertyMap getPropertyMap() {
return PropertyMap.immutableEmptyMap();
}
/**
* Default getSlot implementation. This gets overridden by FunctionType
* for lazily-resolved prototypes.
*/
@Override
public Property getSlot(String name) {
return getPropertyMap().getSlot(name);
}
@Override
public Property getOwnSlot(String name) {
return getPropertyMap().getOwnProperty(name);
}
@Override
public JSType getTypeOfThis() {
return null;
}
/**
* Gets the declared default element type.
* @see TemplatizedType
*/
public ImmutableList<JSType> getTemplateTypes() {
return null;
}
/**
* Gets the docInfo for this type.
*/
@Override
public JSDocInfo getJSDocInfo() {
return docInfo;
}
/**
* Sets the docInfo for this type from the given
* {@link JSDocInfo}. The {@code JSDocInfo} may be {@code null}.
*/
public void setJSDocInfo(JSDocInfo info) {
docInfo = info;
}
/**
* Detects a cycle in the implicit prototype chain. This method accesses
* the {@link #getImplicitPrototype()} method and must therefore be
* invoked only after the object is sufficiently initialized to respond to
* calls to this method.<p>
*
* @return True iff an implicit prototype cycle was detected.
*/
final boolean detectImplicitPrototypeCycle() {
// detecting cycle
this.visited = true;
ObjectType p = getImplicitPrototype();
while (p != null) {
if (p.visited) {
return true;
} else {
p.visited = true;
}
p = p.getImplicitPrototype();
}
// clean up
p = this;
do {
p.visited = false;
p = p.getImplicitPrototype();
} while (p != null);
return false;
}
/**
* Detects cycles in either the implicit prototype chain, or the implemented/extended
* interfaces.<p>
*
* @return True iff a cycle was detected.
*/
final boolean detectInheritanceCycle() {
// TODO(user): This should get moved to preventing cycles in FunctionTypeBuilder
// rather than removing them here after they have been created.
// Also, this doesn't do the right thing for extended interfaces
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>, though that is
// masked by another bug.
return detectImplicitPrototypeCycle()
|| Iterables.contains(this.getCtorImplementedInterfaces(), this)
|| Iterables.contains(this.getCtorExtendedInterfaces(), this);
}
/**
* Gets the reference name for this object. This includes named types
* like constructors, prototypes, and enums. It notably does not include
* literal types like strings and booleans and structural types.
* @return the object's name or {@code null} if this is an anonymous
* object
*/
public abstract String getReferenceName();
/**
* Due to the complexity of some of our internal type systems, sometimes
* we have different types constructed by the same constructor.
* In other parts of the type system, these are called delegates.
* We construct these types by appending suffixes to the constructor name.
*
* The normalized reference name does not have these suffixes, and as such,
* recollapses these implicit types back to their real type.
*/
public String getNormalizedReferenceName() {
String name = getReferenceName();
if (name != null) {
int pos = name.indexOf("(");
if (pos != -1) {
return name.substring(0, pos);
}
}
return name;
}
@Override
public String getDisplayName() {
return getNormalizedReferenceName();
}
/**
* Creates a suffix for a proxy delegate.
* @see #getNormalizedReferenceName
*/
public static String createDelegateSuffix(String suffix) {
return "(" + suffix + ")";
}
/**
* Returns true if the object is named.
* @return true if the object is named, false if it is anonymous
*/
public boolean hasReferenceName() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
// super
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
// objects are comparable to everything but null/undefined
if (that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
} else {
return FALSE;
}
}
/**
* Gets this object's constructor.
* @return this object's constructor or {@code null} if it is a native
* object (constructed natively v.s. by instantiation of a function)
*/
public abstract FunctionType getConstructor();
/**
* Gets the implicit prototype (a.k.a. the {@code [[Prototype]]} property).
*/
public abstract ObjectType getImplicitPrototype();
/**
* Defines a property whose type is explicitly declared by the programmer.
* @param propertyName the property's name
* @param type the type
* @param propertyNode the node corresponding to the declaration of property
* which might later be accessed using {@code getPropertyNode}.
*/
public final boolean defineDeclaredProperty(String propertyName,
JSType type, Node propertyNode) {
boolean result = defineProperty(propertyName, type, false, propertyNode);
// All property definitions go through this method
// or defineInferredProperty. Because the properties defined an an
// object can affect subtyping, it's slightly more efficient
// to
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>param propertyName the name of the property
* @return the {@code Node} corresponding to the property or null.
*/
public Node getPropertyNode(String propertyName) {
Property p = getSlot(propertyName);
return p == null ? null : p.getNode();
}
/**
* Gets the docInfo on the specified property on this type. This should not
* be implemented recursively, as you generally need to know exactly on
* which type in the prototype chain the JSDocInfo exists.
*/
public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) {
Property p = getOwnSlot(propertyName);
return p == null ? null : p.getJSDocInfo();
}
/**
* Sets the docInfo for the specified property from the
* {@link JSDocInfo} on its definition.
* @param info {@code JSDocInfo} for the property definition. May be
* {@code null}.
*/
public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) {
// by default, do nothing
}
@Override
public JSType findPropertyType(String propertyName) {
return hasProperty(propertyName) ?
getPropertyType(propertyName) : null;
}
/**
* Gets the property type of the property whose name is given. If the
* underlying object does not have this property, the Unknown type is
* returned to indicate that no information is available on this property.
*
* This gets overridden by FunctionType for lazily-resolved call() and
* bind() functions.
*
* @return the property's type or {@link UnknownType}. This method never
* returns {@code null}.
*/
public JSType getPropertyType(String propertyName) {
StaticSlot<JSType> slot = getSlot(propertyName);
if (slot == null) {
if (isNoResolvedType() || isCheckedUnknownType()) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
} else if (isEmptyType()) {
return getNativeType(JSTypeNative.NO_TYPE);
}
return getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
return slot.getType();
}
@Override
public boolean hasProperty(String propertyName) {
// Unknown types have all properties.
return isEmptyType() || isUnknownType() || getSlot(propertyName) != null;
}
/**
* Checks whether the property whose name is given is present directly on
* the object. Returns false even if it is declared on a supertype.
*/
public boolean hasOwnProperty(String propertyName) {
return getOwnSlot(propertyName) != null;
}
/**
* Returns the names of all the properties directly on this type.
*
* Overridden by FunctionType to add "prototype".
*/
public Set<String> getOwnPropertyNames() {
return getPropertyMap().getOwnPropertyNames();
}
/**
* Checks whether the property's type is inferred.
*/
public boolean isPropertyTypeInferred(String propertyName) {
StaticSlot<JSType> slot = getSlot(propertyName);
return slot == null ? false : slot.isTypeInferred();
}
/**
* Checks whether the property's type is declared.
*/
public boolean isPropertyTypeDeclared(String propertyName) {
StaticSlot<JS
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Type> slot = getSlot(propertyName);
return slot == null ? false : !slot.isTypeInferred();
}
/**
* Whether the given property is declared on this object.
*/
final boolean hasOwnDeclaredProperty(String name) {
return hasOwnProperty(name) && isPropertyTypeDeclared(name);
}
/** Checks whether the property was defined in the externs. */
public boolean isPropertyInExterns(String propertyName) {
Property p = getSlot(propertyName);
return p == null ? false : p.isFromExterns();
}
/**
* Gets the number of properties of this object.
*/
public int getPropertiesCount() {
return getPropertyMap().getPropertiesCount();
}
/**
* Returns a list of properties defined or inferred on this type and any of
* its supertypes.
*/
public Set<String> getPropertyNames() {
Set<String> props = Sets.newTreeSet();
collectPropertyNames(props);
return props;
}
/**
* Adds any properties defined on this type or its supertypes to the set.
*/
final void collectPropertyNames(Set<String> props) {
getPropertyMap().collectPropertyNames(props);
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseObjectType(this);
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseObjectType(this, that);
}
/**
* Checks that the prototype is an implicit prototype of this object. Since
* each object has an implicit prototype, an implicit prototype's
* implicit prototype is also this implicit prototype's.
*
* @param prototype any prototype based object
*
* @return {@code true} if {@code prototype} is {@code equal} to any
* object in this object's implicit prototype chain.
*/
final boolean isImplicitPrototype(ObjectType prototype) {
for (ObjectType current = this;
current != null;
current = current.getImplicitPrototype()) {
if (current.isTemplatizedType()) {
current = current.toMaybeTemplatizedType().getReferencedType();
}
if (current.isEquivalentTo(prototype)) {
return true;
}
}
return false;
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.TRUE;
}
/**
* We treat this as the unknown type if any of its implicit prototype
* properties is unknown.
*/
@Override
public boolean isUnknownType() {
// If the object is unknown now, check the supertype again,
// because it might have been resolved since the last check.
if (unknown) {
ObjectType implicitProto = getImplicitPrototype();
if (implicitProto == null ||
implicitProto.isNativeObjectType()) {
unknown = false;
for (ObjectType interfaceType : getCtorExtendedInterfaces()) {
if (interfaceType.isUnknownType()) {
unknown = true;
break;
}
}
} else {
unknown = implicitProto.isUnknownType();
}
}
return unknown;
}
@Override
public boolean isObject() {
return true;
}
/**
* Returns true if any cached values have been set for
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> this type. If true,
* then the prototype chain should not be changed, as it might invalidate the
* cached values.
*/
public boolean hasCachedValues() {
return !unknown;
}
/**
* Clear cached values. Should be called before making changes to a prototype
* that may have been changed since creation.
*/
public void clearCachedValues() {
unknown = true;
}
/** Whether this is a built-in object. */
public boolean isNativeObjectType() {
return false;
}
/**
* A null-safe version of JSType#toObjectType.
*/
public static ObjectType cast(JSType type) {
return type == null ? null : type.toObjectType();
}
@Override
public final boolean isFunctionPrototypeType() {
return getOwnerFunction() != null;
}
/** Gets the owner of this if it's a function prototype. */
public FunctionType getOwnerFunction() {
return null;
}
/** Sets the owner function. By default, does nothing. */
void setOwnerFunction(FunctionType type) {}
/**
* Gets the interfaces implemented by the ctor associated with this type.
* Intended to be overridden by subclasses.
*/
public Iterable<ObjectType> getCtorImplementedInterfaces() {
return ImmutableSet.of();
}
/**
* Gets the interfaces extended by the interface associated with this type.
* Intended to be overridden by subclasses.
*/
public Iterable<ObjectType> getCtorExtendedInterfaces() {
return ImmutableSet.of();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
/**
* Group a set of related diagnostic types together, so that they can
* be toggled on and off as one unit.
* @author nicksantos@google.com (Nick Santos)
*/
public class DiagnosticGroup implements Serializable {
private static final long serialVersionUID = 1;
// The set of types represented by this group, hashed by key.
private final Set<DiagnosticType> types;
// A human-readable name for the group.
private final String name;
/**
* Create a group that matches all errors of the given types.
*/
DiagnosticGroup(String name, DiagnosticType ...types) {
this.name = name;
this.types = ImmutableSet.copyOf(Arrays.asList(types));
}
/**
* Create a group that matches all errors of the given types.
*/
public DiagnosticGroup(DiagnosticType ...types) {
this(null, types);
}
/**
* Create a diagnostic group with no name that only matches the given type.
*/
private DiagnosticGroup(DiagnosticType type) {
this.name = null;
this.types = ImmutableSet.of(type);
}
// DiagnosticGroups with only a single DiagnosticType.
private static final Map<DiagnosticType, DiagnosticGroup> singletons =
Maps.newHashMap();
/** Create a diagnostic group that matches only the given type. */
public static DiagnosticGroup forType(DiagnosticType type) {
if (!singletons.containsKey(type)) {
singletons.put(type, new DiagnosticGroup(type));
}
return singletons.get(type);
}
/**
* Create a composite group.
*/
public DiagnosticGroup(DiagnosticGroup ...groups) {
this(null, groups);
}
/**
* Create a composite group.
*/
public DiagnosticGroup(String name, DiagnosticGroup ...groups) {
Set<DiagnosticType> set = Sets.newHashSet();
for (DiagnosticGroup group : groups) {
set.addAll(group.types);
}
this.name = name;
this.types = ImmutableSet.copyOf(set);
}
/**
* Returns whether the given error's type matches a type
* in this group.
*/
public boolean matches(JSError error) {
return matches(error.getType
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>());
}
/**
* Returns whether the given type matches a type in this group.
*/
public boolean matches(DiagnosticType type) {
return types.contains(type);
}
/**
* Returns whether all of the types in the given group are in this group.
*/
boolean isSubGroup(DiagnosticGroup group) {
for (DiagnosticType type : group.types) {
if (!matches(type)) {
return false;
}
}
return true;
}
/**
* Returns an iterable over all the types in this group.
*/
public Iterable<DiagnosticType> getTypes() {
return types;
}
@Override
public String toString() {
return name == null ? super.toString() : "DiagnosticGroup<" + name + ">";
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* An error manager that logs errors and warnings using a logger in addition to
* collecting them in memory. Errors are logged at the SEVERE level and warnings
* are logged at the WARNING level.
*
*/
public class LoggerErrorManager extends BasicErrorManager {
private final MessageFormatter formatter;
private final Logger logger;
/**
* Creates an instance.
*/
public LoggerErrorManager(MessageFormatter formatter, Logger logger) {
this.formatter = formatter;
this.logger = logger;
}
/**
* Creates an instance with a source-less error formatter.
*/
public LoggerErrorManager(Logger logger) {
this(ErrorFormat.SOURCELESS.toFormatter(null, false), logger);
}
@Override
public void println(CheckLevel level, JSError error) {
switch (level) {
case ERROR:
logger.severe(error.format(level, formatter));
break;
case WARNING:
logger.warning(error.format(level, formatter));
break;
case OFF:
break;
}
}
@Override
protected void printSummary() {
Level level = (getErrorCount() + getWarningCount() == 0) ?
Level.INFO : Level.WARNING;
if (getTypedPercent() > 0.0) {
logger.log(level, "{0} error(s), {1} warning(s), {2,number,#.#}% typed",
new Object[] {getErrorCount(), getWarningCount(), getTypedPercent()});
} else {
if (getErrorCount() + getWarningCount() > 0) {
logger.log(level, "{0} error(s), {1} warning(s)",
new Object[] {getErrorCount(), getWarningCount()});
}
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> {@link FunctionType} or
* {@link EnumType}.<p>
*
* If full typedefs are to be supported, then each method on each type class
* needs to be reviewed to make sure that everything works correctly through
* typedefs. Alternatively, we would need to walk through the parse tree and
* unroll each reference to a {@code NamedType} to its resolved type before
* applying the rest of the analysis.<p>
*
* TODO(user): Revisit all of this logic.<p>
*
* The existing typing logic is hacky. Unresolved types should get processed
* in a more consistent way, but with the Rhino merge coming, there will be
* much that has to be changed.<p>
*
*/
class NamedType extends ProxyObjectType {
private static final long serialVersionUID = 1L;
private final String reference;
private final String sourceName;
private final int lineno;
private final int charno;
/**
* Validates the type resolution.
*/
private Predicate<JSType> validator;
/**
* Property-defining continuations.
*/
private List<PropertyContinuation> propertyContinuations = null;
/**
* Create a named type based on the reference.
*/
NamedType(JSTypeRegistry registry, String reference,
String sourceName, int lineno, int charno) {
super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE));
Preconditions.checkNotNull(reference);
this.reference = reference;
this.sourceName = sourceName;
this.lineno = lineno;
this.charno = charno;
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, Node propertyNode) {
if (!isResolved()) {
// If this is an unresolved object type, we need to save all its
// properties and define them when it is resolved.
if (propertyContinuations == null) {
propertyContinuations = Lists.newArrayList();
}
propertyContinuations.add(
new PropertyContinuation(
propertyName, type, inferred, propertyNode));
return true;
} else {
return super.defineProperty(
propertyName, type, inferred, propertyNode);
}
}
private void finishPropertyContinuations() {
ObjectType referencedObjType = getReferencedObjTypeInternal();
if (referencedObjType != null && !referencedObjType.isUnknownType()) {
if (propertyContinuations != null) {
for (PropertyContinuation c : propertyContinuations) {
c.commit(this);
}
}
}
propertyContinuations = null;
}
/** Returns the type to which this refers (which is unknown if unresolved). */
public JSType getReferencedType() {
return getReferencedTypeInternal();
}
@Override
public String getReferenceName() {
return reference;
}
@Override
String toStringHelper(boolean forAnnotations) {
return reference;
}
@Override
public boolean hasReferenceName() {
return true;
}
@Override
boolean isNamedType() {
return true;
}
@Override
public boolean isNominalType() {
return true;
}
@Override
public int hashCode() {
return reference.hashCode();
}
/**
* Resolve the referenced type within the enclosing scope.
*/
@Override
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>length() == 0) {
return null;
}
StaticSlot<JSType> slot = enclosing.getSlot(componentNames[0]);
if (slot == null) {
return null;
}
// If the first component has a type of 'Unknown', then any type
// names using it should be regarded as silently 'Unknown' rather than be
// noisy about it.
JSType slotType = slot.getType();
if (slotType == null || slotType.isAllType() || slotType.isNoType()) {
return null;
}
JSType value = getTypedefType(reporter, slot);
if (value == null) {
return null;
}
// resolving component by component
for (int i = 1; i < componentNames.length; i++) {
ObjectType parentClass = ObjectType.cast(value);
if (parentClass == null) {
return null;
}
if (componentNames[i].length() == 0) {
return null;
}
value = parentClass.getPropertyType(componentNames[i]);
}
return value;
}
private void setReferencedAndResolvedType(
JSType type, ErrorReporter reporter) {
if (validator != null) {
validator.apply(type);
}
setReferencedType(type);
checkEnumElementCycle(reporter);
checkProtoCycle(reporter);
setResolvedTypeInternal(getReferencedType());
}
private void handleTypeCycle(ErrorReporter t) {
setReferencedType(
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE));
warning(t, "Cycle detected in inheritance chain of type " + reference);
setResolvedTypeInternal(getReferencedType());
}
private void checkEnumElementCycle(ErrorReporter t) {
JSType referencedType = getReferencedType();
if (referencedType instanceof EnumElementType &&
((EnumElementType) referencedType).getPrimitiveType() == this) {
handleTypeCycle(t);
}
}
private void checkProtoCycle(ErrorReporter t) {
JSType referencedType = getReferencedType();
if (referencedType == this) {
handleTypeCycle(t);
}
}
// Warns about this type being unresolved iff it's not a forward-declared
// type name.
private void handleUnresolvedType(
ErrorReporter t, boolean ignoreForwardReferencedTypes) {
if (registry.isLastGeneration()) {
boolean isForwardDeclared =
ignoreForwardReferencedTypes &&
registry.isForwardDeclaredType(reference);
if (!isForwardDeclared && registry.isLastGeneration()) {
warning(t, "Bad type annotation. Unknown type " + reference);
} else {
setReferencedType(
registry.getNativeObjectType(
JSTypeNative.NO_RESOLVED_TYPE));
if (registry.isLastGeneration() && validator != null) {
validator.apply(getReferencedType());
}
}
setResolvedTypeInternal(getReferencedType());
} else {
setResolvedTypeInternal(this);
}
}
private JSType getTypedefType(ErrorReporter t, StaticSlot<JSType> slot) {
JSType type = slot.getType();
if (type != null) {
return type;
}
handleUnresolvedType(t, true);
return null;
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> @Override
public boolean setValidator(Predicate<JSType> validator) {
// If the type is already resolved, we can validate it now. If
// the type has not been resolved yet, we need to wait till its
// resolved before we can validate it.
if (this.isResolved()) {
return super.setValidator(validator);
} else {
this.validator = validator;
return true;
}
}
void warning(ErrorReporter reporter, String message) {
reporter.warning(message, sourceName, lineno, charno);
}
/** Store enough information to define a property at a later time. */
private static final class PropertyContinuation {
private final String propertyName;
private final JSType type;
private final boolean inferred;
private final Node propertyNode;
private PropertyContinuation(
String propertyName,
JSType type,
boolean inferred,
Node propertyNode) {
this.propertyName = propertyName;
this.type = type;
this.inferred = inferred;
this.propertyNode = propertyNode;
}
void commit(ObjectType target) {
target.defineProperty(
propertyName, type, inferred, propertyNode);
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2006 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* <p>The syntactic scope creator scans the parse tree to create a Scope object
* containing all the variable declarations in that scope.</p>
*
* <p>This implementation is not thread-safe.</p>
*
*/
class SyntacticScopeCreator implements ScopeCreator {
private final AbstractCompiler compiler;
private Scope scope;
private InputId inputId;
private final RedeclarationHandler redeclarationHandler;
// The arguments variable is special, in that it's declared in every local
// scope, but not explicitly declared.
private static final String ARGUMENTS = "arguments";
/**
* Creates a ScopeCreator.
*/
SyntacticScopeCreator(AbstractCompiler compiler) {
this.compiler = compiler;
this.redeclarationHandler = new DefaultRedeclarationHandler();
}
SyntacticScopeCreator(
AbstractCompiler compiler, RedeclarationHandler redeclarationHandler) {
this.compiler = compiler;
this.redeclarationHandler = redeclarationHandler;
}
@Override
public Scope createScope(Node n, Scope parent) {
inputId = null;
if (parent == null) {
scope = Scope.createGlobalScope(n);
} else {
scope = new Scope(parent, n);
}
scanRoot(n);
inputId = null;
Scope returnedScope = scope;
scope = null;
return returnedScope;
}
private void scanRoot(Node n) {
if (n.isFunction()) {
if (inputId == null) {
inputId = NodeUtil.getInputId(n);
// TODO(johnlenz): inputId maybe null if the FUNCTION node is detached
// from the AST.
// Is it meaningful to build a scope for detached FUNCTION node?
}
final Node fnNameNode = n.getFirstChild();
final Node args = fnNameNode.getNext();
final Node body = args.getNext();
// Bleed the function name into the scope, if it hasn't
// been declared in the outer scope.
String fnName = fnNameNode.getString();
if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) {
declareVar(fnNameNode);
}
// Args: Declare function variables
Preconditions.checkState(args.isParamList());
for (Node a = args.getFirstChild(); a != null;
a = a
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.getNext()) {
Preconditions.checkState(a.isName());
declareVar(a);
}
// Body
scanVars(body);
} else {
// It's the global block
Preconditions.checkState(scope.getParent() == null);
scanVars(n);
}
}
/**
* Scans and gather variables declarations under a Node
*/
private void scanVars(Node n) {
switch (n.getType()) {
case Token.VAR:
// Declare all variables. e.g. var x = 1, y, z;
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
declareVar(child);
child = next;
}
return;
case Token.FUNCTION:
if (NodeUtil.isFunctionExpression(n)) {
return;
}
String fnName = n.getFirstChild().getString();
if (fnName.isEmpty()) {
// This is invalid, but allow it so the checks can catch it.
return;
}
declareVar(n.getFirstChild());
return; // should not examine function's children
case Token.CATCH:
Preconditions.checkState(n.getChildCount() == 2);
Preconditions.checkState(n.getFirstChild().isName());
// the first child is the catch var and the third child
// is the code block
final Node var = n.getFirstChild();
final Node block = var.getNext();
declareVar(var);
scanVars(block);
return; // only one child to scan
case Token.SCRIPT:
inputId = n.getInputId();
Preconditions.checkNotNull(inputId);
break;
}
// Variables can only occur in statement-level nodes, so
// we only need to traverse children in a couple special cases.
if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) {
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
scanVars(child);
child = next;
}
}
}
/**
* Interface for injectable duplicate handling.
*/
interface RedeclarationHandler {
void onRedeclaration(
Scope s, String name, Node n, CompilerInput input);
}
/**
* The default handler for duplicate declarations.
*/
private class DefaultRedeclarationHandler implements RedeclarationHandler {
@Override
public void onRedeclaration(
Scope s, String name, Node n, CompilerInput input) {}
}
/**
* Declares a variable.
*
* @param n The node corresponding to the variable name.
*/
private void declareVar(Node n) {
Preconditions.checkState(n.isName());
CompilerInput input = compiler.getInput(inputId);
String name = n.getString();
if (scope.isDeclared(name, false)
|| (scope.isLocal() && name.equals(ARGUMENTS))) {
redeclarationHandler.onRedeclaration(
scope, name, n, input);
} else {
scope.declare(name, n, null, input);
}
}
/**
* Generates an untyped global scope from the root of AST of compiler (which
* includes externs).
*
* @param compiler The
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> {
DiGraphNode<N, Branch> node = getCfg().getImplicitReturn();
FlowState<L> state = node.getAnnotation();
return state.getIn();
}
@SuppressWarnings("unchecked")
protected L join(L latticeA, L latticeB) {
return joinOp.apply(Lists.<L>newArrayList(latticeA, latticeB));
}
/**
* Checks whether the analysis is a forward flow analysis or backward flow
* analysis.
*
* @return {@code true} if it is a forward analysis.
*/
abstract boolean isForward();
/**
* Computes the output state for a given node and input state.
*
* @param node The node.
* @param input Input lattice that should be read-only.
* @return Output lattice.
*/
abstract L flowThrough(N node, L input);
/**
* Finds a fixed-point solution using at most {@link #MAX_STEPS}
* iterations.
*
* @see #analyze(int)
*/
final void analyze() {
analyze(MAX_STEPS);
}
/**
* Finds a fixed-point solution. The function has the side effect of replacing
* the existing node annotations with the computed solutions using {@link
* com.google.javascript.jscomp.graph.GraphNode#setAnnotation(Annotation)}.
*
* <p>Initially, each node's input and output flow state contains the value
* given by {@link #createInitialEstimateLattice()} (with the exception of the
* entry node of the graph which takes on the {@link #createEntryLattice()}
* value. Each node will use the output state of its predecessor and compute a
* output state according to the instruction. At that time, any nodes that
* depends on the node's newly modified output value will need to recompute
* their output state again. Each step will perform a computation at one node
* until no extra computation will modify any existing output state anymore.
*
* @param maxSteps Max number of iterations before the method stops and throw
* a {@link MaxIterationsExceededException}. This will prevent the
* analysis from going into a infinite loop.
*/
final void analyze(int maxSteps) {
initialize();
int step = 0;
while (!orderedWorkSet.isEmpty()) {
if (step > maxSteps) {
throw new MaxIterationsExceededException(
"Analysis did not terminate after " + maxSteps + " iterations");
}
DiGraphNode<N, Branch> curNode = orderedWorkSet.iterator().next();
orderedWorkSet.remove(curNode);
joinInputs(curNode);
if (flow(curNode)) {
// If there is a change in the current node, we want to grab the list
// of nodes that this node affects.
List<DiGraphNode<N, Branch>> nextNodes = isForward() ?
cfg.getDirectedSuccNodes(curNode) :
cfg.getDirectedPredNodes(curNode);
for (DiGraphNode<N, Branch> nextNode : nextNodes) {
if (nextNode != cfg.getImplicitReturn()) {
orderedWorkSet.add(nextNode);
}
}
}
step++;
}
if (isForward()) {
joinInputs(getCfg().getImplicitReturn());
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
}
}
/**
* Gets the state of the initial estimation at each node.
*
* @return Initial state.
*/
abstract L createInitialEstimateLattice();
/**
* Gets the incoming state of the entry node.
*
* @return Entry state.
*/
abstract L createEntryLattice();
/**
* Initializes the work list and the control flow graph.
*/
protected void initialize() {
// TODO(user): Calling clear doesn't deallocate the memory in a
// LinkedHashSet. Consider creating a new work set if we plan to repeatedly
// call analyze.
orderedWorkSet.clear();
for (DiGraphNode<N, Branch> node : cfg.getDirectedGraphNodes()) {
node.setAnnotation(new FlowState<L>(createInitialEstimateLattice(),
createInitialEstimateLattice()));
if (node != cfg.getImplicitReturn()) {
orderedWorkSet.add(node);
}
}
}
/**
* Performs a single flow through a node.
*
* @return {@code true} if the flow state differs from the previous state.
*/
protected boolean flow(DiGraphNode<N, Branch> node) {
FlowState<L> state = node.getAnnotation();
if (isForward()) {
L outBefore = state.out;
state.out = flowThrough(node.getValue(), state.in);
return !outBefore.equals(state.out);
} else {
L inBefore = state.in;
state.in = flowThrough(node.getValue(), state.out);
return !inBefore.equals(state.in);
}
}
/**
* Computes the new flow state at a given node's entry by merging the
* output (input) lattice of the node's predecessor (successor).
*
* @param node Node to compute new join.
*/
protected void joinInputs(DiGraphNode<N, Branch> node) {
FlowState<L> state = node.getAnnotation();
if (isForward()) {
if (cfg.getEntry() == node) {
state.setIn(createEntryLattice());
} else {
List<DiGraphNode<N, Branch>> inNodes = cfg.getDirectedPredNodes(node);
if (inNodes.size() == 1) {
FlowState<L> inNodeState = inNodes.get(0).getAnnotation();
state.setIn(inNodeState.getOut());
} else if (inNodes.size() > 1) {
List<L> values = new ArrayList<L>(inNodes.size());
for (DiGraphNode<N, Branch> currentNode : inNodes) {
FlowState<L> currentNodeState = currentNode.getAnnotation();
values.add(currentNodeState.getOut());
}
state.setIn(joinOp.apply(values));
}
}
} else {
List<DiGraphNode<N, Branch>> inNodes = cfg.getDirectedSuccNodes(node);
if (inNodes.size() == 1) {
DiGraphNode<N, Branch> inNode = inNodes.get(0);
if (inNode == cfg.getImplicitReturn()) {
state.setOut(createEntryLattice());
} else {
FlowState<
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>L> inNodeState = inNode.getAnnotation();
state.setOut(inNodeState.getIn());
}
} else if (inNodes.size() > 1) {
List<L> values = new ArrayList<L>(inNodes.size());
for (DiGraphNode<N, Branch> currentNode : inNodes) {
FlowState<L> currentNodeState = currentNode.getAnnotation();
values.add(currentNodeState.getIn());
}
state.setOut(joinOp.apply(values));
}
}
}
/**
* The in and out states of a node.
*
* @param <L> Input and output lattice element type.
*/
static class FlowState<L extends LatticeElement> implements Annotation {
private L in;
private L out;
/**
* Private constructor. No other classes should create new states.
*
* @param inState Input.
* @param outState Output.
*/
private FlowState(L inState, L outState) {
Preconditions.checkNotNull(inState);
Preconditions.checkNotNull(outState);
this.in = inState;
this.out = outState;
}
L getIn() {
return in;
}
void setIn(L in) {
Preconditions.checkNotNull(in);
this.in = in;
}
L getOut() {
return out;
}
void setOut(L out) {
Preconditions.checkNotNull(out);
this.out = out;
}
@Override
public String toString() {
return String.format("IN: %s OUT: %s", in, out);
}
@Override
public int hashCode() {
return Objects.hashCode(in, out);
}
}
/**
* The exception to be thrown if the analysis has been running for a long
* number of iterations. Chances are the analysis is not monotonic, a
* fixed-point cannot be found and it is currently stuck in an infinite loop.
*/
static class MaxIterationsExceededException extends RuntimeException {
private static final long serialVersionUID = 1L;
MaxIterationsExceededException(String msg) {
super(msg);
}
}
abstract static class BranchedForwardDataFlowAnalysis
<N, L extends LatticeElement> extends DataFlowAnalysis<N, L> {
@Override
protected void initialize() {
orderedWorkSet.clear();
for (DiGraphNode<N, Branch> node : getCfg().getDirectedGraphNodes()) {
int outEdgeCount = getCfg().getOutEdges(node.getValue()).size();
List<L> outLattices = Lists.newArrayList();
for (int i = 0; i < outEdgeCount; i++) {
outLattices.add(createInitialEstimateLattice());
}
node.setAnnotation(new BranchedFlowState<L>(
createInitialEstimateLattice(), outLattices));
if (node != getCfg().getImplicitReturn()) {
orderedWorkSet.add(node);
}
}
}
BranchedForwardDataFlowAnalysis(ControlFlowGraph<N> targetCfg,
JoinOp<L> joinOp) {
super(targetCfg, joinOp);
}
/**
* Returns the lattice element at the exit point. Needs to be overridden
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> * because we use a BranchedFlowState instead of a FlowState; ugh.
*/
@Override
L getExitLatticeElement() {
DiGraphNode<N, Branch> node = getCfg().getImplicitReturn();
BranchedFlowState<L> state = node.getAnnotation();
return state.getIn();
}
@Override
final boolean isForward() {
return true;
}
/**
* The branched flow function maps a single lattice to a list of output
* lattices.
*
* <p>Each outgoing edge of a node will have a corresponding output lattice
* in the ordered returned by
* {@link com.google.javascript.jscomp.graph.DiGraph#getOutEdges(Object)}
* in the returned list.
*
* @return A list of output values depending on the edge's branch type.
*/
abstract List<L> branchedFlowThrough(N node, L input);
@Override
protected final boolean flow(DiGraphNode<N, Branch> node) {
BranchedFlowState<L> state = node.getAnnotation();
List<L> outBefore = state.out;
state.out = branchedFlowThrough(node.getValue(), state.in);
Preconditions.checkState(outBefore.size() == state.out.size());
for (int i = 0; i < outBefore.size(); i++) {
if (!outBefore.get(i).equals(state.out.get(i))) {
return true;
}
}
return false;
}
@Override
protected void joinInputs(DiGraphNode<N, Branch> node) {
BranchedFlowState<L> state = node.getAnnotation();
List<DiGraphNode<N, Branch>> predNodes =
getCfg().getDirectedPredNodes(node);
List<L> values = new ArrayList<L>(predNodes.size());
for (DiGraphNode<N, Branch> predNode : predNodes) {
BranchedFlowState<L> predNodeState = predNode.getAnnotation();
L in = predNodeState.out.get(
getCfg().getDirectedSuccNodes(predNode).indexOf(node));
values.add(in);
}
if (getCfg().getEntry() == node) {
state.setIn(createEntryLattice());
} else if (!values.isEmpty()) {
state.setIn(joinOp.apply(values));
}
}
}
/**
* The in and out states of a node.
*
* @param <L> Input and output lattice element type.
*/
static class BranchedFlowState<L extends LatticeElement>
implements Annotation {
private L in;
private List<L> out;
/**
* Private constructor. No other classes should create new states.
*
* @param inState Input.
* @param outState Output.
*/
private BranchedFlowState(L inState, List<L> outState) {
Preconditions.checkNotNull(inState);
Preconditions.checkNotNull(outState);
this.in = inState;
this.out = outState;
}
L getIn() {
return in;
}
void setIn(L in) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> Preconditions.checkNotNull(in);
this.in = in;
}
List<L> getOut() {
return out;
}
void setOut(List<L> out) {
Preconditions.checkNotNull(out);
for (L item : out) {
Preconditions.checkNotNull(item);
}
this.out = out;
}
@Override
public String toString() {
return String.format("IN: %s OUT: %s", in, out);
}
@Override
public int hashCode() {
return Objects.hashCode(in, out);
}
}
/**
* Compute set of escaped variables. When a variable is escaped in a
* dataflow analysis, it can be reference outside of the code that we are
* analyzing. A variable is escaped if any of the following is true:
*
* <p><ol>
* <li>It is defined as the exception name in CATCH clause so it became a
* variable local not to our definition of scope.</li>
* <li>Exported variables as they can be needed after the script terminates.
* </li>
* <li>Names of named functions because in JavaScript, <i>function foo(){}</i>
* does not kill <i>foo</i> in the dataflow.</li>
*/
static void computeEscaped(final Scope jsScope, final Set<Var> escaped,
AbstractCompiler compiler) {
// TODO(user): Very good place to store this information somewhere.
AbstractPostOrderCallback finder = new AbstractPostOrderCallback() {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (jsScope == t.getScope() || !n.isName()
|| parent.isFunction()) {
return;
}
String name = n.getString();
Var var = t.getScope().getVar(name);
if (var != null && var.scope == jsScope) {
escaped.add(jsScope.getVar(name));
}
}
};
NodeTraversal t = new NodeTraversal(compiler, finder);
t.traverseAtScope(jsScope);
// 1: Remove the exception name in CATCH which technically isn't local to
// begin with.
for (Iterator<Var> i = jsScope.getVars(); i.hasNext();) {
Var var = i.next();
if (var.getParentNode().isCatch() ||
compiler.getCodingConvention().isExported(var.getName())) {
escaped.add(var);
}
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2011 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
/**
* {@link CheckDebuggerStatement} checks for the presence of the "debugger"
* statement in JavaScript code. It is appropriate to use this statement while
* developing JavaScript; however, it is generally undesirable to include it in
* production code.
*
* @author bolinfest@google.com (Michael Bolin)
*/
class CheckDebuggerStatement extends AbstractPostOrderCallback
implements CompilerPass {
static final DiagnosticType DEBUGGER_STATEMENT_PRESENT =
DiagnosticType.disabled("JSC_DEBUGGER_STATEMENT_PRESENT",
"Using the debugger statement can halt your application if the user " +
"has a JavaScript debugger running.");
private final AbstractCompiler compiler;
public CheckDebuggerStatement(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isDebugger()) {
t.report(n, DEBUGGER_STATEMENT_PRESENT);
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt;
/**
* Error formats available.
*/
public enum ErrorFormat {
LEGACY {
@Override
public MessageFormatter toFormatter(
SourceExcerptProvider source, boolean colorize) {
VerboseMessageFormatter formatter = new VerboseMessageFormatter(source);
formatter.setColorize(colorize);
return formatter;
}
},
SINGLELINE {
@Override
public MessageFormatter toFormatter(
SourceExcerptProvider source, boolean colorize) {
LightweightMessageFormatter formatter = new LightweightMessageFormatter(
source);
formatter.setColorize(colorize);
return formatter;
}
},
MULTILINE {
@Override
public MessageFormatter toFormatter(
SourceExcerptProvider source, boolean colorize) {
LightweightMessageFormatter formatter = new LightweightMessageFormatter(
source, SourceExcerpt.REGION);
formatter.setColorize(colorize);
return formatter;
}
},
SOURCELESS {
@Override
public MessageFormatter toFormatter(
SourceExcerptProvider source, boolean colorize) {
LightweightMessageFormatter formatter =
LightweightMessageFormatter.withoutSource();
formatter.setColorize(colorize);
return formatter;
}
};
/**
* Convert to a concrete formatter.
*/
public abstract MessageFormatter toFormatter(
SourceExcerptProvider source, boolean colorize);
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> final DiagnosticType BAD_FUNCTION_DECLARATION = DiagnosticType.error(
"JSC_BAD_FUNCTION_DECLARATION",
"functions can only be declared at top level or immediately within " +
"another function in ES5 strict mode");
private final AbstractCompiler compiler;
private final boolean noVarCheck;
private final boolean noCajaChecks;
StrictModeCheck(AbstractCompiler compiler) {
this(compiler, false, false);
}
StrictModeCheck(
AbstractCompiler compiler, boolean noVarCheck, boolean noCajaChecks) {
this.compiler = compiler;
this.noVarCheck = noVarCheck;
this.noCajaChecks = noCajaChecks;
}
@Override public void process(Node externs, Node root) {
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
NodeTraversal.traverse(compiler, root, new NonExternChecks());
}
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isFunction()) {
checkFunctionUse(t, n);
} else if (n.isName()) {
if (!isDeclaration(n)) {
checkNameUse(t, n);
}
} else if (n.isAssign()) {
checkAssignment(t, n);
} else if (n.isDelProp()) {
checkDelete(t, n);
} else if (n.isObjectLit()) {
checkObjectLiteral(t, n);
} else if (n.isLabel()) {
checkLabel(t, n);
}
}
/** Checks that the function is used legally. */
private void checkFunctionUse(NodeTraversal t, Node n) {
if (NodeUtil.isFunctionDeclaration(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) {
t.report(n, BAD_FUNCTION_DECLARATION);
}
}
/**
* Determines if the given name is a declaration, which can be a declaration
* of a variable, function, or argument.
*/
private static boolean isDeclaration(Node n) {
switch (n.getParent().getType()) {
case Token.VAR:
case Token.FUNCTION:
case Token.CATCH:
return true;
case Token.PARAM_LIST:
return n.getParent().getParent().isFunction();
default:
return false;
}
}
/** Checks that the given name is used legally. */
private void checkNameUse(NodeTraversal t, Node n) {
Var v = t.getScope().getVar(n.getString());
if (v == null) {
// In particular, this prevents creating a global variable by assigning
// to it without a declaration.
if (!noVarCheck) {
t.report(n, UNKNOWN_VARIABLE, n.getString());
}
}
if (!noCajaChecks) {
if ("eval".equals(n.getString())) {
t.report(n, EVAL_USE);
} else if (n.getString().endsWith("__")) {
t.report(n, ILLEGAL_NAME);
}
}
}
/** Checks that an assignment is not to the "arguments" object. */
private void checkAssignment(NodeTraversal t, Node n) {
if (n
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.getFirstChild().isName()) {
if ("arguments".equals(n.getFirstChild().getString())) {
t.report(n, ARGUMENTS_ASSIGNMENT);
} else if ("eval".equals(n.getFirstChild().getString())) {
// Note that assignment to eval is already illegal because any use of
// that name is illegal.
if (noCajaChecks) {
t.report(n, EVAL_ASSIGNMENT);
}
}
}
}
/** Checks that variables, functions, and arguments are not deleted. */
private void checkDelete(NodeTraversal t, Node n) {
if (n.getFirstChild().isName()) {
Var v = t.getScope().getVar(n.getFirstChild().getString());
if (v != null) {
t.report(n, DELETE_VARIABLE);
}
}
}
/** Checks that object literal keys are valid. */
private void checkObjectLiteral(NodeTraversal t, Node n) {
Set<String> getters = Sets.newHashSet();
Set<String> setters = Sets.newHashSet();
for (Node key = n.getFirstChild();
key != null;
key = key.getNext()) {
if (!noCajaChecks && key.getString().endsWith("__")) {
t.report(key, ILLEGAL_NAME);
}
if (!key.isSetterDef()) {
// normal property and getter cases
if (getters.contains(key.getString())) {
t.report(key, DUPLICATE_OBJECT_KEY);
} else {
getters.add(key.getString());
}
}
if (!key.isGetterDef()) {
// normal property and setter cases
if (setters.contains(key.getString())) {
t.report(key, DUPLICATE_OBJECT_KEY);
} else {
setters.add(key.getString());
}
}
}
}
/** Checks that label names are valid. */
private void checkLabel(NodeTraversal t, Node n) {
if (n.getFirstChild().getString().endsWith("__")) {
if (!noCajaChecks) {
t.report(n.getFirstChild(), ILLEGAL_NAME);
}
}
}
/** Checks that are performed on non-extern code only. */
private class NonExternChecks extends AbstractPostOrderCallback {
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if ((n.isName()) && isDeclaration(n)) {
checkDeclaration(t, n);
} else if (n.isGetProp()) {
checkProperty(t, n);
}
}
/** Checks for illegal declarations. */
private void checkDeclaration(NodeTraversal t, Node n) {
if ("eval".equals(n.getString())) {
t.report(n, EVAL_DECLARATION);
} else if ("arguments".equals(n.getString())) {
t.report(n, ARGUMENTS_DECLARATION);
} else if (n.getString().endsWith("__")) {
if (!noCajaChecks) {
t.report(n, ILLEGAL_NAME);
}
}
}
/** Checks for illegal property accesses. */
private void checkProperty(NodeTraversal t, Node n) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
/**
* Bottom type, representing the subclass of any value or object.
*
* Although JavaScript programmers can't explicitly denote the bottom type,
* it comes up in static analysis. For example, if we have:
* <code>
* var x = null;
* if (x) {
* f(x);
* }
* </code>
* We need to be able to assign {@code x} a type within the {@code f(x)}
* call. Since it has no possible type, we assign {@code x} the NoType,
* so that {@code f(x)} is legal no matter what the type of {@code f}'s
* first argument is.
*
* @see <a href="http://en.wikipedia.org/wiki/Bottom_type">Bottom types</a>
*/
public class NoType extends NoObjectType {
private static final long serialVersionUID = 1L;
NoType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNoObjectType() {
return false;
}
@Override
public boolean isNoType() {
return true;
}
@Override
public boolean isNullable() {
return true;
}
@Override
public boolean isSubtype(JSType that) {
return true;
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
return BooleanLiteralSet.EMPTY;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNoType();
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseNoType(that);
}
@Override
String toStringHelper(boolean forAnnotations) {
return forAnnotations ? "?" : "None";
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> new EnumElementType(registry, elementsType, name);
}
/**
* Gets the source node or null if this is an unknown enum.
*/
public Node getSource() {
return source;
}
@Override
public EnumType toMaybeEnumType() {
return this;
}
@Override
public ObjectType getImplicitPrototype() {
return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
}
/**
* Gets the elements defined on this enum.
* @return the elements' names defined on this enum. The returned set is
* immutable.
*/
public Set<String> getElements() {
return Collections.unmodifiableSet(elements);
}
/**
* Defines a new element on this enum.
* @param name the name of the new element
* @param definingNode the {@code Node} that defines this new element
* @return true iff the new element is added successfully
*/
public boolean defineElement(String name, Node definingNode) {
elements.add(name);
return defineDeclaredProperty(name, elementsType, definingNode);
}
/**
* Gets the elements' type.
*/
public EnumElementType getElementsType() {
return elementsType;
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
return this.isEquivalentTo(that) ? TRUE : FALSE;
}
@Override
public boolean isSubtype(JSType that) {
return that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_TYPE)) ||
that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_PROTOTYPE)) ||
JSType.isSubtypeHelper(this, that);
}
@Override
String toStringHelper(boolean forAnnotations) {
return forAnnotations ? "Object" : getReferenceName();
}
@Override
public String getDisplayName() {
return elementsType.getDisplayName();
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseObjectType(this);
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseObjectType(this, that);
}
@Override
public FunctionType getConstructor() {
return null;
}
@Override
public boolean matchesNumberContext() {
return false;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
elementsType = (EnumElementType) elementsType.resolve(t, scope);
return super.resolveInternal(t, scope);
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>INITIALIZED_USING_NEW_SYNTAX =
DiagnosticType.error("JSC_MSG_NOT_INITIALIZED_USING_NEW_SYNTAX",
"message not initialized using " + MSG_FUNCTION_NAME);
static final DiagnosticType BAD_FALLBACK_SYNTAX =
DiagnosticType.error("JSC_MSG_BAD_FALLBACK_SYNTAX",
String.format(
"Bad syntax. " +
"Expected syntax: goog.getMsgWithFallback(MSG_1, MSG_2)",
MSG_FALLBACK_FUNCTION_NAME));
static final DiagnosticType FALLBACK_ARG_ERROR =
DiagnosticType.error("JSC_MSG_FALLBACK_ARG_ERROR",
"Could not find message entry for fallback argument {0}");
private static final String PH_JS_PREFIX = "{$";
private static final String PH_JS_SUFFIX = "}";
static final String MSG_PREFIX = "MSG_";
/**
* Pattern for unnamed messages.
* <p>
* All JS messages in JS code should have unique name but messages in
* generated code (i.e. from soy template) could have duplicated message names.
* Later we replace the message names with ids constructed as a hash of the
* message content.
* <p>
* <link href="http://code.google.com/p/closure-templates/">
* Soy</link> generates messages with names MSG_UNNAMED_<NUMBER> . This
* pattern recognizes such messages.
*/
private static final Pattern MSG_UNNAMED_PATTERN =
Pattern.compile("MSG_UNNAMED_\\d+");
private static final Pattern CAMELCASE_PATTERN =
Pattern.compile("[a-z][a-zA-Z\\d]*[_\\d]*");
static final String HIDDEN_DESC_PREFIX = "@hidden";
// For old-style JS messages
private static final String DESC_SUFFIX = "_HELP";
private final boolean needToCheckDuplications;
private final JsMessage.Style style;
private final JsMessage.IdGenerator idGenerator;
final AbstractCompiler compiler;
/**
* The names encountered associated with their defining node and source. We
* use it for tracking duplicated message ids in the source code.
*/
private final Map<String, MessageLocation> messageNames =
Maps.newHashMap();
private final Map<Var, JsMessage> unnamedMessages =
Maps.newHashMap();
/**
* List of found goog.getMsg call nodes.
*
* When we visit goog.getMsg() node we add a pair node:sourcename and later
* when we visit its parent we remove this pair. All nodes that are left at
* the end of traversing are orphaned nodes. It means have no corresponding
* var or property node.
*/
private final Map<Node, String> googMsgNodes = Maps.newHashMap();
private final CheckLevel checkLevel;
/**
* Creates JS message visitor.
*
* @param compiler the compiler instance
* @param needToCheckDuplications whether to check duplicated messages in
* traversed
* @param style style that should be used during parsing
* @param idGenerator generator that used for creating unique ID for the
* message
*/
JsMessageVisitor(AbstractCompiler compiler,
boolean needToCheckDuplic
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>ations,
JsMessage.Style style, JsMessage.IdGenerator idGenerator) {
this.compiler = compiler;
this.needToCheckDuplications = needToCheckDuplications;
this.style = style;
this.idGenerator = idGenerator;
checkLevel = (style == JsMessage.Style.CLOSURE)
? CheckLevel.ERROR : CheckLevel.WARNING;
// TODO(anatol): add flag that decides whether to process UNNAMED messages.
// Some projects would not want such functionality (unnamed) as they don't
// use SOY templates.
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
for (Map.Entry<Node, String> msgNode : googMsgNodes.entrySet()) {
compiler.report(JSError.make(msgNode.getValue(), msgNode.getKey(),
checkLevel, MESSAGE_NODE_IS_ORPHANED));
}
}
@Override
public void visit(NodeTraversal traversal, Node node, Node parent) {
String messageKey;
boolean isVar;
Node msgNode, msgNodeParent;
switch (node.getType()) {
case Token.NAME:
// var MSG_HELLO = 'Message'
if ((parent != null) && (parent.isVar())) {
messageKey = node.getString();
isVar = true;
} else {
return;
}
msgNode = node.getFirstChild();
msgNodeParent = node;
break;
case Token.ASSIGN:
// somenamespace.someclass.MSG_HELLO = 'Message'
isVar = false;
Node getProp = node.getFirstChild();
if (!getProp.isGetProp()) {
return;
}
Node propNode = getProp.getLastChild();
messageKey = propNode.getString();
msgNode = node.getLastChild();
msgNodeParent = node;
break;
case Token.CALL:
// goog.getMsg()
String fnName = node.getFirstChild().getQualifiedName();
if (MSG_FUNCTION_NAME.equals(fnName)) {
googMsgNodes.put(node, traversal.getSourceName());
} else if (MSG_FALLBACK_FUNCTION_NAME.equals(fnName)) {
visitFallbackFunctionCall(traversal, node);
}
return;
default:
return;
}
// Is this a message name?
boolean isNewStyleMessage =
msgNode != null && msgNode.isCall();
if (!isMessageName(messageKey, isNewStyleMessage)) {
return;
}
if (msgNode == null) {
compiler.report(
traversal.makeError(node, MESSAGE_HAS_NO_VALUE, messageKey));
return;
}
// Just report a warning if a qualified messageKey that looks like a message
// (e.g. "a.b.MSG_X") doesn't use goog.getMsg().
if (isNewStyleMessage) {
googMsgNodes.remove(msgNode);
} else if (style != JsMessage.Style.LEGACY) {
compiler.report(traversal.makeError(node, checkLevel,
MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX));
}
boolean isUnnamedMsg = is
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
* @return String representation of the node
* @throws MalformedException if the parsed message is invalid
*/
private static String extractStringFromStringExprNode(Node node)
throws MalformedException {
switch (node.getType()) {
case Token.STRING:
return node.getString();
case Token.ADD:
StringBuilder sb = new StringBuilder();
for (Node child : node.children()) {
sb.append(extractStringFromStringExprNode(child));
}
return sb.toString();
default:
throw new MalformedException("STRING or ADD node expected; found: " +
getReadableTokenName(node), node);
}
}
/**
* Initializes a message builder from a FUNCTION node.
* <p>
* <pre>
* The tree should look something like:
*
* function
* |-- name
* |-- lp
* | |-- name <arg1>
* | -- name <arg2>
* -- block
* |
* --return
* |
* --add
* |-- string foo
* -- name <arg1>
* </pre>
*
* @param builder the message builder
* @param node the function node that contains a message
* @throws MalformedException if the parsed message is invalid
*/
private void extractFromFunctionNode(Builder builder, Node node)
throws MalformedException {
Set<String> phNames = Sets.newHashSet();
for (Node fnChild : node.children()) {
switch (fnChild.getType()) {
case Token.NAME:
// This is okay. The function has a name, but it is empty.
break;
case Token.PARAM_LIST:
// Parse the placeholder names from the function argument list.
for (Node argumentNode : fnChild.children()) {
if (argumentNode.isName()) {
String phName = argumentNode.getString();
if (phNames.contains(phName)) {
throw new MalformedException("Duplicate placeholder name: "
+ phName, argumentNode);
} else {
phNames.add(phName);
}
}
}
break;
case Token.BLOCK:
// Build the message's value by examining the return statement
Node returnNode = fnChild.getFirstChild();
if (!returnNode.isReturn()) {
throw new MalformedException("RETURN node expected; found: "
+ getReadableTokenName(returnNode), returnNode);
}
for (Node child : returnNode.children()) {
extractFromReturnDescendant(builder, child);
}
// Check that all placeholders from the message text have appropriate
// object literal keys
for (String phName : builder.getPlaceholders()) {
if (!phNames.contains(phName)) {
throw new MalformedException(
"Unrecognized message placeholder referenced: " + phName,
returnNode);
}
}
break;
default:
throw new MalformedException(
"NAME, LP, or BLOCK node expected; found: "
+ getReadableTokenName(node), fnChild);
}
}
}
/**
* Appends value parts to the message builder by traversing the descendants
* of the given RETURN node.
*
* @param builder the message builder
* @
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> phName, aNode);
}
if (phNames.contains(phName)) {
throw new MalformedException("Duplicate placeholder name: "
+ phName, aNode);
}
phNames.add(phName);
}
}
// Check that all placeholders from the message text have appropriate objlit
// values
Set<String> usedPlaceholders = builder.getPlaceholders();
for (String phName : usedPlaceholders) {
if (!phNames.contains(phName)) {
throw new MalformedException(
"Unrecognized message placeholder referenced: " + phName,
objLitNode);
}
}
// Check that objLiteral have only names that are present in the
// message text
for (String phName : phNames) {
if (!usedPlaceholders.contains(phName)) {
throw new MalformedException(
"Unused message placeholder: " + phName,
objLitNode);
}
}
}
/**
* Appends the message parts in a JS message value extracted from the given
* text node.
*
* @param builder the JS message builder to append parts to
* @param node the node with string literal that contains the message text
* @throws MalformedException if {@code value} contains a reference to
* an unregistered placeholder
*/
private void parseMessageTextNode(Builder builder, Node node)
throws MalformedException {
String value = extractStringFromStringExprNode(node);
while (true) {
int phBegin = value.indexOf(PH_JS_PREFIX);
if (phBegin < 0) {
// Just a string literal
builder.appendStringPart(value);
return;
} else {
if (phBegin > 0) {
// A string literal followed by a placeholder
builder.appendStringPart(value.substring(0, phBegin));
}
// A placeholder. Find where it ends
int phEnd = value.indexOf(PH_JS_SUFFIX, phBegin);
if (phEnd < 0) {
throw new MalformedException(
"Placeholder incorrectly formatted in: " + builder.getKey(),
node);
}
String phName = value.substring(phBegin + PH_JS_PREFIX.length(),
phEnd);
builder.appendPlaceholderReference(phName);
int nextPos = phEnd + PH_JS_SUFFIX.length();
if (nextPos < value.length()) {
// Iterate on the rest of the message value
value = value.substring(nextPos);
} else {
// The message is parsed
return;
}
}
}
}
/** Visit a call to goog.getMsgWithFallback. */
private void visitFallbackFunctionCall(NodeTraversal t, Node call) {
// Check to make sure the function call looks like:
// goog.getMsgWithFallback(MSG_1, MSG_2);
if (call.getChildCount() != 3 ||
!call.getChildAtIndex(1).isName() ||
!call.getChildAtIndex(2).isName()) {
compiler.report(t.makeError(call, BAD_FALLBACK_SYNTAX));
return;
}
Node firstArg = call.getChildAtIndex(1);
JsMessage firstMessage = getTrackedMessage(t, firstArg.getString());
if (
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>pre>
* Foo.a;
* Bar.a;
* </pre>
*
* <p> will become
*
* <pre>
* Foo.a$Foo;
* Bar.a$Bar;
* </pre>
*
*/
class DisambiguateProperties<T> implements CompilerPass {
// To prevent the logs from filling up, we cap the number of warnings
// that we tell the user to fix per-property.
private static final int MAX_INVALDIATION_WARNINGS_PER_PROPERTY = 10;
private static final Logger logger = Logger.getLogger(
DisambiguateProperties.class.getName());
static class Warnings {
// TODO(user): {1} and {2} are not exactly useful for most people.
static final DiagnosticType INVALIDATION = DiagnosticType.disabled(
"JSC_INVALIDATION",
"Property disambiguator skipping all instances of property {0} "
+ "because of type {1} node {2}. {3}");
static final DiagnosticType INVALIDATION_ON_TYPE = DiagnosticType.disabled(
"JSC_INVALIDATION_TYPE",
"Property disambiguator skipping instances of property {0} "
+ "on type {1}. {2}");
}
private final AbstractCompiler compiler;
private final TypeSystem<T> typeSystem;
/**
* Map of a type to all the related errors that invalidated the type
* for disambiguation. It has be Object because of the generic nature of
* this pass.
*/
private Multimap<Object, JSError> invalidationMap;
/**
* In practice any large code base will have thousands and thousands of
* type invalidations, which makes reporting all of the errors useless.
* However, certain properties are worth specifically guarding because of the
* large amount of code that can be removed as dead code. This list contains
* the properties (eg: "toString") that we care about; if any of these
* properties is invalidated it causes an error.
*/
private final Map<String, CheckLevel> propertiesToErrorFor;
private class Property {
/** The name of the property. */
final String name;
/** All types on which the field exists, grouped together if related. */
private UnionFind<T> types;
/**
* A set of types for which renaming this field should be skipped. This
* list is first filled by fields defined in the externs file.
*/
Set<T> typesToSkip = Sets.newHashSet();
/**
* If true, do not rename any instance of this field, as it has been
* referenced from an unknown type.
*/
boolean skipRenaming;
/** Set of nodes for this field that need renaming. */
Set<Node> renameNodes = Sets.newHashSet();
/**
* Map from node to the highest type in the prototype chain containing the
* field for that node. In the case of a union, the type is the highest type
* of one of the types in the union.
*/
final Map<Node, T> rootTypes = Maps.newHashMap();
Property(String name) {
this.name = name;
}
/** Returns the types on which this field is referenced. */
UnionFind<T> getTypes
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>() {
if (types == null) {
types = new StandardUnionFind<T>();
}
return types;
}
/**
* Record that this property is referenced from this type.
* @return true if the type was recorded for this property, else false,
* which would happen if the type was invalidating.
*/
boolean addType(T type, T top, T relatedType) {
checkState(!skipRenaming, "Attempt to record skipped property: %s", name);
if (typeSystem.isInvalidatingType(top)) {
invalidate();
return false;
} else {
if (typeSystem.isTypeToSkip(top)) {
addTypeToSkip(top);
}
if (relatedType == null) {
getTypes().add(top);
} else {
getTypes().union(top, relatedType);
}
typeSystem.recordInterfaces(type, top, this);
return true;
}
}
/** Records the given type as one to skip for this property. */
void addTypeToSkip(T type) {
for (T skipType : typeSystem.getTypesToSkipForType(type)) {
typesToSkip.add(skipType);
getTypes().union(skipType, type);
}
}
/** Invalidates any types related to invalid types. */
void expandTypesToSkip() {
// If we are not going to rename any properties, then we do not need to
// update the list of invalid types, as they are all invalid.
if (shouldRename()) {
int count = 0;
while (true) {
// It should usually only take one time through this do-while.
checkState(++count < 10, "Stuck in loop expanding types to skip.");
// Make sure that the representative type for each type to skip is
// marked as being skipped.
Set<T> rootTypesToSkip = Sets.newHashSet();
for (T subType : typesToSkip) {
rootTypesToSkip.add(types.find(subType));
}
typesToSkip.addAll(rootTypesToSkip);
Set<T> newTypesToSkip = Sets.newHashSet();
Set<T> allTypes = types.elements();
int originalTypesSize = allTypes.size();
for (T subType : allTypes) {
if (!typesToSkip.contains(subType)
&& typesToSkip.contains(types.find(subType))) {
newTypesToSkip.add(subType);
}
}
for (T newType : newTypesToSkip) {
addTypeToSkip(newType);
}
// If there were not any new types added, we are done here.
if (types.elements().size() == originalTypesSize) {
break;
}
}
}
}
/** Returns true if any instance of this property should be renamed. */
boolean shouldRename() {
return !skipRenaming && types != null
&& types.allEquivalenceClasses().size() > 1;
}
/**
* Returns true if this property should be renamed on this type.
* expandTypesToSkip() should be called before this, if anything has been
* added to the typesToSkip list.
*/
boolean shouldRename(T
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> type) {
return !skipRenaming && !typesToSkip.contains(type);
}
/**
* Invalidates a field from renaming. Used for field references on an
* object with unknown type.
*/
boolean invalidate() {
boolean changed = !skipRenaming;
skipRenaming = true;
types = null;
return changed;
}
/**
* Schedule the node to potentially be renamed.
* @param node the node to rename
* @param type the highest type in the prototype chain for which the
* property is defined
* @return True if type was accepted without invalidation or if the property
* was already invalidated. False if this property was invalidated this
* time.
*/
boolean scheduleRenaming(Node node, T type) {
if (!skipRenaming) {
if (typeSystem.isInvalidatingType(type)) {
invalidate();
return false;
}
renameNodes.add(node);
rootTypes.put(node, type);
}
return true;
}
}
private Map<String, Property> properties = Maps.newHashMap();
static DisambiguateProperties<JSType> forJSTypeSystem(
AbstractCompiler compiler,
Map<String, CheckLevel> propertiesToErrorFor) {
return new DisambiguateProperties<JSType>(
compiler, new JSTypeSystem(compiler), propertiesToErrorFor);
}
static DisambiguateProperties<ConcreteType> forConcreteTypeSystem(
AbstractCompiler compiler, TightenTypes tt,
Map<String, CheckLevel> propertiesToErrorFor) {
return new DisambiguateProperties<ConcreteType>(
compiler, new ConcreteTypeSystem(tt, compiler.getCodingConvention()),
propertiesToErrorFor);
}
/**
* This constructor should only be called by one of the helper functions
* above for either the JSType system, or the concrete type system.
*/
private DisambiguateProperties(AbstractCompiler compiler,
TypeSystem<T> typeSystem, Map<String, CheckLevel> propertiesToErrorFor) {
this.compiler = compiler;
this.typeSystem = typeSystem;
this.propertiesToErrorFor = propertiesToErrorFor;
if (!this.propertiesToErrorFor.isEmpty()) {
this.invalidationMap = LinkedHashMultimap.create();
} else {
this.invalidationMap = null;
}
}
@Override
public void process(Node externs, Node root) {
Preconditions.checkState(
compiler.getLifeCycleStage() == LifeCycleStage.NORMALIZED);
for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) {
addInvalidatingType(mis.typeA, mis.src);
addInvalidatingType(mis.typeB, mis.src);
}
NodeTraversal.traverse(compiler, externs, new FindExternProperties());
NodeTraversal.traverse(compiler, root, new FindRenameableProperties());
renameProperties();
}
private void recordInvalidationError(JSType t, JSError error) {
if (!t.isObject()) {
return;
}
if (invalidationMap != null) {
invalidationMap.put(t, error);
}
}
/**
* Invalidates the given type, so that no properties on it
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> will be renamed.
*/
private void addInvalidatingType(JSType type, JSError error) {
type = type.restrictByNotNullOrUndefined();
if (type.isUnionType()) {
for (JSType alt : type.toMaybeUnionType().getAlternates()) {
addInvalidatingType(alt, error);
}
} else if (type.isEnumElementType()) {
addInvalidatingType(
type.toMaybeEnumElementType().getPrimitiveType(), error);
} else {
typeSystem.addInvalidatingType(type);
recordInvalidationError(type, error);
ObjectType objType = ObjectType.cast(type);
if (objType != null && objType.getImplicitPrototype() != null) {
typeSystem.addInvalidatingType(objType.getImplicitPrototype());
recordInvalidationError(objType.getImplicitPrototype(), error);
}
}
}
/** Returns the property for the given name, creating it if necessary. */
protected Property getProperty(String name) {
if (!properties.containsKey(name)) {
properties.put(name, new Property(name));
}
return properties.get(name);
}
/** Public for testing. */
T getTypeWithProperty(String field, T type) {
return typeSystem.getTypeWithProperty(field, type);
}
/** Tracks the current type system scope while traversing. */
private abstract class AbstractScopingCallback implements ScopedCallback {
protected final Stack<StaticScope<T>> scopes =
new Stack<StaticScope<T>>();
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
@Override
public void enterScope(NodeTraversal t) {
if (t.inGlobalScope()) {
scopes.push(typeSystem.getRootScope());
} else {
scopes.push(typeSystem.getFunctionScope(t.getScopeRoot()));
}
}
@Override
public void exitScope(NodeTraversal t) {
scopes.pop();
}
/** Returns the current scope at this point in the file. */
protected StaticScope<T> getScope() {
return scopes.peek();
}
}
/**
* Finds all properties defined in the externs file and sets them as
* ineligible for renaming from the type on which they are defined.
*/
private class FindExternProperties extends AbstractScopingCallback {
@Override public void visit(NodeTraversal t, Node n, Node parent) {
// TODO(johnlenz): Support object-literal property definitions.
if (n.isGetProp()) {
String field = n.getLastChild().getString();
T type = typeSystem.getType(getScope(), n.getFirstChild(), field);
Property prop = getProperty(field);
if (typeSystem.isInvalidatingType(type)) {
prop.invalidate();
} else {
prop.addTypeToSkip(type);
// If this is a prototype property, then we want to skip assignments
// to the instance type as well. These assignments are not usually
// seen in the extern code itself, so we must handle them here.
if ((type = typeSystem.getInstanceFromPrototype(type)) != null) {
prop.getTypes().add(type);
prop.typesTo
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Skip.add(type);
}
}
}
}
}
/**
* Traverses the tree, building a map from field names to Nodes for all
* fields that can be renamed.
*/
private class FindRenameableProperties extends AbstractScopingCallback {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isGetProp()) {
handleGetProp(t, n);
} else if (n.isObjectLit()) {
handleObjectLit(t, n);
}
}
/**
* Processes a GETPROP node.
*/
private void handleGetProp(NodeTraversal t, Node n) {
String name = n.getLastChild().getString();
T type = typeSystem.getType(getScope(), n.getFirstChild(), name);
Property prop = getProperty(name);
if (!prop.scheduleRenaming(n.getLastChild(),
processProperty(t, prop, type, null))) {
if (propertiesToErrorFor.containsKey(name)) {
String suggestion = "";
if (type instanceof JSType) {
JSType jsType = (JSType) type;
if (jsType.isAllType() || jsType.isUnknownType()) {
if (n.getFirstChild().isThis()) {
suggestion = "The \"this\" object is unknown in the function," +
"consider using @this";
} else {
String qName = n.getFirstChild().getQualifiedName();
suggestion = "Consider casting " + qName +
" if you know it's type.";
}
} else {
List<String> errors = Lists.newArrayList();
printErrorLocations(errors, jsType);
if (!errors.isEmpty()) {
suggestion = "Consider fixing errors for the following types:\n";
suggestion += Joiner.on("\n").join(errors);
}
}
}
compiler.report(JSError.make(
t.getSourceName(), n, propertiesToErrorFor.get(name),
Warnings.INVALIDATION, name,
(type == null ? "null" : type.toString()),
n.toString(), suggestion));
}
}
}
/**
* Processes a OBJECTLIT node.
*/
private void handleObjectLit(NodeTraversal t, Node n) {
for (Node child = n.getFirstChild();
child != null;
child = child.getNext()) {
// Maybe STRING, GET, SET
if (child.isQuotedString()) {
continue;
}
// We should never see a mix of numbers and strings.
String name = child.getString();
T type = typeSystem.getType(getScope(), n, name);
Property prop = getProperty(name);
if (!prop.scheduleRenaming(child,
processProperty(t, prop, type, null))) {
// TODO(user): It doesn't look like the user can do much in this
// case right now.
if (propertiesToErrorFor.containsKey(name)) {
compiler.report(JSError.make(
t.getSourceName(), child, propertiesToErrorFor.get(name),
Warnings.INVALIDATION, name,
(type == null ? "null" : type.toString()), n.toString(), ""));
}
}
}
}
private void printErrorLocations(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>List<String> errors, JSType t) {
if (!t.isObject() || t.isAllType()) {
return;
}
if (t.isUnionType()) {
for (JSType alt : t.toMaybeUnionType().getAlternates()) {
printErrorLocations(errors, alt);
}
return;
}
for (JSError error : invalidationMap.get(t)) {
if (errors.size() > MAX_INVALDIATION_WARNINGS_PER_PROPERTY) {
return;
}
errors.add(
t.toString() + " at " + error.sourceName + ":" + error.lineNumber);
}
}
/**
* Processes a property, adding it to the list of properties to rename.
* @return a representative type for the property reference, which will be
* the highest type on the prototype chain of the provided type. In the
* case of a union type, it will be the highest type on the prototype
* chain of one of the members of the union.
*/
private T processProperty(
NodeTraversal t, Property prop, T type, T relatedType) {
type = typeSystem.restrictByNotNullOrUndefined(type);
if (prop.skipRenaming || typeSystem.isInvalidatingType(type)) {
return null;
}
Iterable<T> alternatives = typeSystem.getTypeAlternatives(type);
if (alternatives != null) {
T firstType = relatedType;
for (T subType : alternatives) {
T lastType = processProperty(t, prop, subType, firstType);
if (lastType != null) {
firstType = firstType == null ? lastType : firstType;
}
}
return firstType;
} else {
T topType = typeSystem.getTypeWithProperty(prop.name, type);
if (typeSystem.isInvalidatingType(topType)) {
return null;
}
prop.addType(type, topType, relatedType);
return topType;
}
}
}
/** Renames all properties with references on more than one type. */
void renameProperties() {
int propsRenamed = 0, propsSkipped = 0, instancesRenamed = 0,
instancesSkipped = 0, singleTypeProps = 0;
Set<String> reported = Sets.newHashSet();
for (Property prop : properties.values()) {
if (prop.shouldRename()) {
Map<T, String> propNames = buildPropNames(prop.getTypes(), prop.name);
++propsRenamed;
prop.expandTypesToSkip();
for (Node node : prop.renameNodes) {
T rootType = prop.rootTypes.get(node);
if (prop.shouldRename(rootType)) {
String newName = propNames.get(rootType);
node.setString(newName);
compiler.reportCodeChange();
++instancesRenamed;
} else {
++instancesSkipped;
CheckLevel checkLevelForProp = propertiesToErrorFor.get(prop.name);
if (checkLevelForProp != null &&
checkLevelForProp != CheckLevel.OFF &&
!reported.contains(prop.name)) {
reported.add(prop.name);
compiler.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>report(JSError.make(
NodeUtil.getSourceName(node), node,
checkLevelForProp,
Warnings.INVALIDATION_ON_TYPE, prop.name,
rootType.toString(), ""));
}
}
}
} else {
if (prop.skipRenaming) {
++propsSkipped;
} else {
++singleTypeProps;
}
}
}
logger.fine("Renamed " + instancesRenamed + " instances of "
+ propsRenamed + " properties.");
logger.fine("Skipped renaming " + instancesSkipped + " invalidated "
+ "properties, " + propsSkipped + " instances of properties "
+ "that were skipped for specific types and " + singleTypeProps
+ " properties that were referenced from only one type.");
}
/**
* Chooses a name to use for renaming in each equivalence class and maps
* each type in that class to it.
*/
private Map<T, String> buildPropNames(UnionFind<T> types, String name) {
Map<T, String> names = Maps.newHashMap();
for (Set<T> set : types.allEquivalenceClasses()) {
checkState(!set.isEmpty());
String typeName = null;
for (T type : set) {
if (typeName == null || type.toString().compareTo(typeName) < 0) {
typeName = type.toString();
}
}
String newName;
if ("{...}".equals(typeName)) {
newName = name;
} else {
newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name;
}
for (T type : set) {
names.put(type, newName);
}
}
return names;
}
/** Returns a map from field name to types for which it will be renamed. */
Multimap<String, Collection<T>> getRenamedTypesForTesting() {
Multimap<String, Collection<T>> ret = HashMultimap.create();
for (Map.Entry<String, Property> entry : properties.entrySet()) {
Property prop = entry.getValue();
if (!prop.skipRenaming) {
for (Collection<T> c : prop.getTypes().allEquivalenceClasses()) {
if (!c.isEmpty() && !prop.typesToSkip.contains(c.iterator().next())) {
ret.put(entry.getKey(), c);
}
}
}
}
return ret;
}
/** Interface for providing the type information needed by this pass. */
private interface TypeSystem<T> {
// TODO(user): add a getUniqueName(T type) method that is guaranteed
// to be unique, performant and human-readable.
/** Returns the top-most scope used by the type system (if any). */
StaticScope<T> getRootScope();
/** Returns the new scope started at the given function node. */
StaticScope<T> getFunctionScope(Node node);
/**
* Returns the type of the given node.
* @param prop Only types with this property need to be returned. In general
* with type tightening, this will require no special processing, but in
* the case of an unknown JSType, we might need to add in the native
* types since we don't track
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> them, but only if they have the given
* property.
*/
T getType(StaticScope<T> scope, Node node, String prop);
/**
* Returns true if a field reference on this type will invalidate all
* references to that field as candidates for renaming. This is true if the
* type is unknown or all-inclusive, as variables with such a type could be
* references to any object.
*/
boolean isInvalidatingType(T type);
/**
* Informs the given type system that a type is invalidating due to a type
* mismatch found during type checking.
*/
void addInvalidatingType(JSType type);
/**
* Returns a set of types that should be skipped given the given type.
* This is necessary for interfaces when using JSTypes, as all super
* interfaces must also be skipped.
*/
ImmutableSet<T> getTypesToSkipForType(T type);
/**
* Determines whether the given type is one whose properties should not be
* considered for renaming.
*/
boolean isTypeToSkip(T type);
/** Remove null and undefined from the options in the given type. */
T restrictByNotNullOrUndefined(T type);
/**
* Returns the alternatives if this is a type that represents multiple
* types, and null if not. Union and interface types can correspond to
* multiple other types.
*/
Iterable<T> getTypeAlternatives(T type);
/**
* Returns the type in the chain from the given type that contains the given
* field or null if it is not found anywhere.
*/
T getTypeWithProperty(String field, T type);
/**
* Returns the type of the instance of which this is the prototype or null
* if this is not a function prototype.
*/
T getInstanceFromPrototype(T type);
/**
* Records that this property could be referenced from any interface that
* this type, or any type in its superclass chain, implements.
*/
void recordInterfaces(T type, T relatedType,
DisambiguateProperties<T>.Property p);
}
/** Implementation of TypeSystem using JSTypes. */
private static class JSTypeSystem implements TypeSystem<JSType> {
private final Set<JSType> invalidatingTypes;
private JSTypeRegistry registry;
public JSTypeSystem(AbstractCompiler compiler) {
registry = compiler.getTypeRegistry();
invalidatingTypes = Sets.newHashSet(
registry.getNativeType(JSTypeNative.ALL_TYPE),
registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE),
registry.getNativeType(JSTypeNative.NO_TYPE),
registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE),
registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE),
registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE),
registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE),
registry.getNativeType(JSTypeNative.UNKNOWN_TYPE));
}
@Override public void addInvalidatingType(JSType type) {
checkState(!type.isUnionType());
invalidatingTypes.add(type);
}
@Override public StaticScope<JSType> getRootScope() { return null; }
@Override public StaticScope<JSType>
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> getFunctionScope(Node node) {
return null;
}
@Override public JSType getType(
StaticScope<JSType> scope, Node node, String prop) {
if (node.getJSType() == null) {
return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
return node.getJSType();
}
@Override public boolean isInvalidatingType(JSType type) {
if (type == null || invalidatingTypes.contains(type) ||
type.isUnknownType() /* unresolved types */) {
return true;
}
ObjectType objType = ObjectType.cast(type);
return objType != null && !objType.hasReferenceName();
}
@Override public ImmutableSet<JSType> getTypesToSkipForType(JSType type) {
type = type.restrictByNotNullOrUndefined();
if (type.isUnionType()) {
Set<JSType> types = Sets.newHashSet(type);
for (JSType alt : type.toMaybeUnionType().getAlternates()) {
types.addAll(getTypesToSkipForTypeNonUnion(alt));
}
return ImmutableSet.copyOf(types);
} else if (type.isEnumElementType()) {
return getTypesToSkipForType(
type.toMaybeEnumElementType().getPrimitiveType());
}
return ImmutableSet.copyOf(getTypesToSkipForTypeNonUnion(type));
}
private Set<JSType> getTypesToSkipForTypeNonUnion(JSType type) {
Set<JSType> types = Sets.newHashSet();
JSType skipType = type;
while (skipType != null) {
types.add(skipType);
ObjectType objSkipType = skipType.toObjectType();
if (objSkipType != null) {
skipType = objSkipType.getImplicitPrototype();
} else {
break;
}
}
return types;
}
@Override public boolean isTypeToSkip(JSType type) {
return type.isEnumType() || (type.autoboxesTo() != null);
}
@Override public JSType restrictByNotNullOrUndefined(JSType type) {
return type.restrictByNotNullOrUndefined();
}
@Override public Iterable<JSType> getTypeAlternatives(JSType type) {
if (type.isUnionType()) {
return type.toMaybeUnionType().getAlternates();
} else {
ObjectType objType = type.toObjectType();
if (objType != null &&
objType.getConstructor() != null &&
objType.getConstructor().isInterface()) {
List<JSType> list = Lists.newArrayList();
for (FunctionType impl
: registry.getDirectImplementors(objType)) {
list.add(impl.getInstanceType());
}
return list;
} else {
return null;
}
}
}
@Override public ObjectType getTypeWithProperty(String field, JSType type) {
if (type == null) {
return null;
}
if (type.isEnumElementType()) {
return getTypeWithProperty(
field, type.toMaybeEnumElementType().getPrimitiveType());
}
if (!(type instanceof ObjectType)) {
if (type.autoboxesTo() != null) {
type = type.autoboxesTo();
} else {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> return null;
}
}
// Ignore the prototype itself at all times.
if ("prototype".equals(field)) {
return null;
}
// We look up the prototype chain to find the highest place (if any) that
// this appears. This will make references to overridden properties look
// like references to the initial property, so they are renamed alike.
ObjectType foundType = null;
ObjectType objType = ObjectType.cast(type);
if (objType != null && objType.getConstructor() != null
&& objType.getConstructor().isInterface()) {
ObjectType topInterface = FunctionType.getTopDefiningInterface(
objType, field);
if (topInterface != null && topInterface.getConstructor() != null) {
foundType = topInterface.getConstructor().getPrototype();
}
} else {
while (objType != null && objType.getImplicitPrototype() != objType) {
if (objType.hasOwnProperty(field)) {
foundType = objType;
}
objType = objType.getImplicitPrototype();
}
}
// If the property does not exist on the referenced type but the original
// type is an object type, see if any subtype has the property.
if (foundType == null) {
ObjectType maybeType = ObjectType.cast(
registry.getGreatestSubtypeWithProperty(type, field));
// getGreatestSubtypeWithProperty does not guarantee that the property
// is defined on the returned type, it just indicates that it might be,
// so we have to double check.
if (maybeType != null && maybeType.hasOwnProperty(field)) {
foundType = maybeType;
}
}
return foundType;
}
@Override public JSType getInstanceFromPrototype(JSType type) {
if (type.isFunctionPrototypeType()) {
ObjectType prototype = (ObjectType) type;
FunctionType owner = prototype.getOwnerFunction();
if (owner.isConstructor() || owner.isInterface()) {
return prototype.getOwnerFunction().getInstanceType();
}
}
return null;
}
@Override
public void recordInterfaces(JSType type, JSType relatedType,
DisambiguateProperties<JSType>.Property p) {
ObjectType objType = ObjectType.cast(type);
if (objType != null) {
FunctionType constructor;
if (objType.isFunctionType()) {
constructor = objType.toMaybeFunctionType();
} else if (objType.isFunctionPrototypeType()) {
constructor = objType.getOwnerFunction();
} else {
constructor = objType.getConstructor();
}
while (constructor != null) {
for (ObjectType itype : constructor.getImplementedInterfaces()) {
JSType top = getTypeWithProperty(p.name, itype);
if (top != null) {
p.addType(itype, top, relatedType);
} else {
recordInterfaces(itype, relatedType, p);
}
// If this interface invalidated this property, return now.
if (p.skipRenaming) {
return;
}
}
if (constructor.isInterface() || constructor.isConstructor()) {
constructor = constructor.getSuperClassConstructor();
} else {
constructor = null;
}
}
}
}
}
/** Implementation of TypeSystem using
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> concrete types. */
private static class ConcreteTypeSystem implements TypeSystem<ConcreteType> {
private final TightenTypes tt;
private int nextUniqueId;
private CodingConvention codingConvention;
private final Set<JSType> invalidatingTypes = Sets.newHashSet();
// An array of native types that are not tracked by type tightening, and
// thus need to be added in if an unknown type is encountered.
private static final JSTypeNative [] nativeTypes = new JSTypeNative[] {
JSTypeNative.BOOLEAN_OBJECT_TYPE,
JSTypeNative.NUMBER_OBJECT_TYPE,
JSTypeNative.STRING_OBJECT_TYPE
};
public ConcreteTypeSystem(TightenTypes tt, CodingConvention convention) {
this.tt = tt;
this.codingConvention = convention;
}
@Override public void addInvalidatingType(JSType type) {
checkState(!type.isUnionType());
invalidatingTypes.add(type);
}
@Override public StaticScope<ConcreteType> getRootScope() {
return tt.getTopScope();
}
@Override public StaticScope<ConcreteType> getFunctionScope(Node decl) {
ConcreteFunctionType func = tt.getConcreteFunction(decl);
return (func != null) ?
func.getScope() : (StaticScope<ConcreteType>) null;
}
@Override
public ConcreteType getType(
StaticScope<ConcreteType> scope, Node node, String prop) {
if (scope != null) {
ConcreteType c = tt.inferConcreteType(
(TightenTypes.ConcreteScope) scope, node);
return maybeAddAutoboxes(c, node, prop);
} else {
return null;
}
}
/**
* Add concrete types for autoboxing types if necessary. The concrete type
* system does not track native types, like string, so add them if they are
* present in the JSType for the node.
*/
private ConcreteType maybeAddAutoboxes(
ConcreteType cType, Node node, String prop) {
JSType jsType = node.getJSType();
if (jsType == null) {
return cType;
} else if (jsType.isUnknownType()) {
for (JSTypeNative nativeType : nativeTypes) {
ConcreteType concrete = tt.getConcreteInstance(
tt.getTypeRegistry().getNativeObjectType(nativeType));
if (concrete != null && !concrete.getPropertyType(prop).isNone()) {
cType = cType.unionWith(concrete);
}
}
return cType;
}
return maybeAddAutoboxes(cType, jsType, prop);
}
private ConcreteType maybeAddAutoboxes(
ConcreteType cType, JSType jsType, String prop) {
jsType = jsType.restrictByNotNullOrUndefined();
if (jsType.isUnionType()) {
for (JSType alt : jsType.toMaybeUnionType().getAlternates()) {
cType = maybeAddAutoboxes(cType, alt, prop);
}
return cType;
} else if (jsType.isEnumElementType()) {
return maybeAddAutoboxes(
cType, jsType.toMaybeEnumElementType().getPrimitiveType(), prop);
}
if (jsType.autoboxesTo() != null) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> JSType autoboxed = jsType.autoboxesTo();
return cType.unionWith(tt.getConcreteInstance((ObjectType) autoboxed));
} else if (jsType.unboxesTo() != null) {
return cType.unionWith(tt.getConcreteInstance((ObjectType) jsType));
}
return cType;
}
@Override public boolean isInvalidatingType(ConcreteType type) {
// We will disallow types on functions so that 'prototype' is not renamed.
// TODO(user): Support properties on functions as well.
return (type == null) || type.isAll() || type.isFunction()
|| (type.isInstance()
&& invalidatingTypes.contains(type.toInstance().instanceType));
}
@Override
public ImmutableSet<ConcreteType> getTypesToSkipForType(ConcreteType type) {
return ImmutableSet.of(type);
}
@Override public boolean isTypeToSkip(ConcreteType type) {
// Skip anonymous object literals and enum types.
return type.isInstance()
&& !(type.toInstance().isFunctionPrototype()
|| type.toInstance().instanceType.isInstanceType());
}
@Override
public ConcreteType restrictByNotNullOrUndefined(ConcreteType type) {
// These are not represented in concrete types.
return type;
}
@Override
public Iterable<ConcreteType> getTypeAlternatives(ConcreteType type) {
if (type.isUnion()) {
return ((ConcreteUnionType) type).getAlternatives();
} else {
return null;
}
}
@Override public ConcreteType getTypeWithProperty(String field,
ConcreteType type) {
if (type.isInstance()) {
ConcreteInstanceType instanceType = (ConcreteInstanceType) type;
return instanceType.getInstanceTypeWithProperty(field);
} else if (type.isFunction()) {
if ("prototype".equals(field)
|| codingConvention.isSuperClassReference(field)) {
return type;
}
} else if (type.isNone()) {
// If the receiver is none, then this code is never reached. We will
// return a new fake type to ensure that this access is renamed
// differently from any other, so it can be easily removed.
return new ConcreteUniqueType(++nextUniqueId);
} else if (type.isUnion()) {
// If only one has the property, return that.
for (ConcreteType t : ((ConcreteUnionType) type).getAlternatives()) {
ConcreteType ret = getTypeWithProperty(field, t);
if (ret != null) {
return ret;
}
}
}
return null;
}
@Override public ConcreteType getInstanceFromPrototype(ConcreteType type) {
if (type.isInstance()) {
ConcreteInstanceType instanceType = (ConcreteInstanceType) type;
if (instanceType.isFunctionPrototype()) {
return instanceType.getConstructorType().getInstanceType();
}
}
return null;
}
@Override
public void recordInterfaces(ConcreteType type, ConcreteType relatedType,
DisambiguateProperties<ConcreteType>.Property p) {
// No need to record interfaces when using concrete types.
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
/**
* The error manager is in charge of storing, organizing and displaying
* errors and warnings generated by the compiler.
*
*/
public interface ErrorManager extends ErrorHandler {
/**
* Reports an error. The errors will be displayed by the
* {@link #generateReport()} at the discretion of the implementation.
*
* @param level the reporting level
* @param error the error to report
*/
@Override
void report(CheckLevel level, JSError error);
/**
* Writes a report to an implementation-specific medium. The compiler calls
* this method after any and all {@link #report} calls.
*/
void generateReport();
/**
* Gets the number of reported errors.
*/
int getErrorCount();
/**
* Gets the number of reported warnings.
*/
int getWarningCount();
/**
* Gets all the errors.
*/
JSError[] getErrors();
/**
* Gets all the warnings.
*/
JSError[] getWarnings();
/**
* Sets the percentage of typed expressions.
*/
void setTypedPercent(double typedPercent);
/**
* Gets the percentage of typed expressions.
*/
double getTypedPercent();
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* Check for usage of 'with'.
*
*/
class ControlStructureCheck implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
static final DiagnosticType USE_OF_WITH = DiagnosticType.warning(
"JSC_USE_OF_WITH",
"The use of the 'with' structure should be avoided.");
ControlStructureCheck(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
check(root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
check(scriptRoot);
}
/**
* Reports errors for any invalid use of control structures.
*
* @param node Current node to check.
*/
private void check(Node node) {
switch (node.getType()) {
case Token.WITH:
JSDocInfo info = node.getJSDocInfo();
boolean allowWith =
info != null && info.getSuppressions().contains("with");
if (!allowWith) {
report(node, USE_OF_WITH);
}
break;
}
for (Node bChild = node.getFirstChild(); bChild != null;) {
Node next = bChild.getNext();
check(bChild);
bChild = next;
}
}
private void report(Node n, DiagnosticType error) {
compiler.report(JSError.make(n.getSourceFileName(), n, error));
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.collect.ImmutableList;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import java.util.Collections;
/**
* An object type which uses composition to delegate all calls.
*
* @see NamedType
* @see TemplatizedType
*
*/
class ProxyObjectType extends ObjectType {
private static final long serialVersionUID = 1L;
private JSType referencedType;
private ObjectType referencedObjType;
ProxyObjectType(JSTypeRegistry registry, JSType referencedType) {
this(registry, referencedType, null);
}
ProxyObjectType(JSTypeRegistry registry, JSType referencedType,
TemplateTypeMap templateTypeMap) {
super(registry, templateTypeMap);
setReferencedType(referencedType);
}
@Override
PropertyMap getPropertyMap() {
return referencedObjType == null
? PropertyMap.immutableEmptyMap() : referencedObjType.getPropertyMap();
}
JSType getReferencedTypeInternal() {
return referencedType;
}
ObjectType getReferencedObjTypeInternal() {
return referencedObjType;
}
void setReferencedType(JSType referencedType) {
this.referencedType = referencedType;
if (referencedType instanceof ObjectType) {
this.referencedObjType = (ObjectType) referencedType;
} else {
this.referenced
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>ObjType = null;
}
}
@Override
public String getReferenceName() {
return referencedObjType == null ?
"" : referencedObjType.getReferenceName();
}
@Override
public boolean hasReferenceName() {
return referencedObjType == null ?
null : referencedObjType.hasReferenceName();
}
@Override
public boolean matchesNumberContext() {
return referencedType.matchesNumberContext();
}
@Override
public boolean matchesStringContext() {
return referencedType.matchesStringContext();
}
@Override
public boolean matchesObjectContext() {
return referencedType.matchesObjectContext();
}
@Override
public boolean canBeCalled() {
return referencedType.canBeCalled();
}
@Override
public boolean isNoType() {
return referencedType.isNoType();
}
@Override
public boolean isNoObjectType() {
return referencedType.isNoObjectType();
}
@Override
public boolean isNoResolvedType() {
return referencedType.isNoResolvedType();
}
@Override
public boolean isUnknownType() {
return referencedType.isUnknownType();
}
@Override
public boolean isCheckedUnknownType() {
return referencedType.isCheckedUnknownType();
}
@Override
public boolean isNullable() {
return referencedType.isNullable();
}
@Override
public EnumType toMaybeEnumType() {
return referencedType.toMaybeEnumType();
}
@Override
public boolean isConstructor() {
return referencedType.isConstructor();
}
@Override
public boolean isNominalType() {
return referencedType.isNominalType();
}
@Override
public boolean isInstanceType() {
return referencedType.isInstanceType();
}
@Override
public boolean isInterface() {
return referencedType.isInterface();
}
@Override
public boolean isOrdinaryFunction() {
return referencedType.isOrdinaryFunction();
}
@Override
public boolean isAllType() {
return referencedType.isAllType();
}
@Override
public boolean isStruct() {
return referencedType.isStruct();
}
@Override
public boolean isDict() {
return referencedType.isDict();
}
@Override
public boolean isNativeObjectType() {
return referencedObjType == null
? false : referencedObjType.isNativeObjectType();
}
@Override
RecordType toMaybeRecordType() {
return referencedType.toMaybeRecordType();
}
@Override
public UnionType toMaybeUnionType() {
return referencedType.toMaybeUnionType();
}
@Override
public FunctionType toMaybeFunctionType() {
return referencedType.toMaybeFunctionType();
}
@Override
public EnumElementType toMaybeEnumElementType() {
return referencedType.toMaybeEnumElementType();
}
@Override
public TernaryValue testForEquality(JSType that) {
return referencedType.testForEquality(that);
}
@Override
public boolean isSubtype(JSType that) {
return referencedType.isSubtype(that);
}
@Override
public FunctionType getOwnerFunction() {
return referencedObjType == null
? null : referencedObjType.getOwnerFunction();
}
@Override
public Iterable<ObjectType> getCtorImplementedInterfaces() {
return referencedObjType == null ? Collections.<ObjectType>emptyList() :
referencedObjType
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.getCtorImplementedInterfaces();
}
@Override
public int hashCode() {
return referencedType.hashCode();
}
@Override
String toStringHelper(boolean forAnnotations) {
return referencedType.toStringHelper(forAnnotations);
}
@Override
public ObjectType getImplicitPrototype() {
return referencedObjType == null ? null :
referencedObjType.getImplicitPrototype();
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, Node propertyNode) {
return referencedObjType == null ? true :
referencedObjType.defineProperty(
propertyName, type, inferred, propertyNode);
}
@Override
public boolean removeProperty(String name) {
return referencedObjType == null ? false :
referencedObjType.removeProperty(name);
}
@Override
public JSType findPropertyType(String propertyName) {
return referencedType.findPropertyType(propertyName);
}
@Override
public JSDocInfo getJSDocInfo() {
return referencedType.getJSDocInfo();
}
@Override
public void setJSDocInfo(JSDocInfo info) {
if (referencedObjType != null) {
referencedObjType.setJSDocInfo(info);
}
}
@Override
public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) {
if (referencedObjType != null) {
referencedObjType.setPropertyJSDocInfo(propertyName, info);
}
}
@Override
public FunctionType getConstructor() {
return referencedObjType == null ? null :
referencedObjType.getConstructor();
}
@Override
public ImmutableList<JSType> getTemplateTypes() {
return referencedObjType == null ? null :
referencedObjType.getTemplateTypes();
}
@Override
public <T> T visit(Visitor<T> visitor) {
return referencedType.visit(visitor);
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return referencedType.visit(visitor, that);
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setReferencedType(referencedType.resolve(t, scope));
return this;
}
@Override
public String toDebugHashCodeString() {
return "{proxy:" + referencedType.toDebugHashCodeString() + "}";
}
@Override
public JSType getTypeOfThis() {
if (referencedObjType != null) {
return referencedObjType.getTypeOfThis();
}
return super.getTypeOfThis();
}
@Override
public JSType collapseUnion() {
if (referencedType.isUnionType()) {
return referencedType.collapseUnion();
}
return this;
}
@Override
public void matchConstraint(JSType constraint) {
referencedType.matchConstraint(constraint);
}
@Override
public TemplatizedType toMaybeTemplatizedType() {
return referencedType.toMaybeTemplatizedType();
}
@Override
public TemplateType toMaybeTemplateType() {
return referencedType.toMaybeTemplateType();
}
@Override
public boolean hasAnyTemplateTypesInternal() {
return referencedType.hasAnyTemplateTypes();
}
@Override
public TemplateTypeMap getTemplateTypeMap() {
return referencedType.getTemplateTypeMap();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>_DEPRECATED_CLASS_REASON",
"Class {0} has been deprecated: {1}");
static final DiagnosticType BAD_PRIVATE_GLOBAL_ACCESS =
DiagnosticType.disabled(
"JSC_BAD_PRIVATE_GLOBAL_ACCESS",
"Access to private variable {0} not allowed outside file {1}.");
static final DiagnosticType BAD_PRIVATE_PROPERTY_ACCESS =
DiagnosticType.disabled(
"JSC_BAD_PRIVATE_PROPERTY_ACCESS",
"Access to private property {0} of {1} not allowed here.");
static final DiagnosticType BAD_PROTECTED_PROPERTY_ACCESS =
DiagnosticType.disabled(
"JSC_BAD_PROTECTED_PROPERTY_ACCESS",
"Access to protected property {0} of {1} not allowed here.");
static final DiagnosticType PRIVATE_OVERRIDE =
DiagnosticType.disabled(
"JSC_PRIVATE_OVERRIDE",
"Overriding private property of {0}.");
static final DiagnosticType EXTEND_FINAL_CLASS =
DiagnosticType.error(
"JSC_EXTEND_FINAL_CLASS",
"{0} is not allowed to extend final class {1}.");
static final DiagnosticType VISIBILITY_MISMATCH =
DiagnosticType.disabled(
"JSC_VISIBILITY_MISMATCH",
"Overriding {0} property of {1} with {2} property.");
static final DiagnosticType CONST_PROPERTY_REASSIGNED_VALUE =
DiagnosticType.warning(
"JSC_CONSTANT_PROPERTY_REASSIGNED_VALUE",
"constant property {0} assigned a value more than once");
static final DiagnosticType CONST_PROPERTY_DELETED =
DiagnosticType.warning(
"JSC_CONSTANT_PROPERTY_DELETED",
"constant property {0} cannot be deleted");
private final AbstractCompiler compiler;
private final TypeValidator validator;
// State about the current traversal.
private int deprecatedDepth = 0;
private int methodDepth = 0;
private JSType currentClass = null;
private final Multimap<String, String> initializedConstantProperties;
CheckAccessControls(AbstractCompiler compiler) {
this.compiler = compiler;
this.validator = compiler.getTypeValidator();
this.initializedConstantProperties = HashMultimap.create();
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
NodeTraversal.traverse(compiler, scriptRoot, this);
}
@Override
public void enterScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
Node parent = n.getParent();
if (isDeprecatedFunction(n)) {
deprecatedDepth++;
}
if (methodDepth == 0) {
currentClass = getClassOfMethod(n, parent);
}
methodDepth++;
}
}
@Override
public void exitScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
if (isDeprecatedFunction(n)) {
deprecatedDepth--;
}
methodDepth--;
if (methodDepth == 0) {
currentClass = null;
}
}
}
/**
* Gets the type of the class
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> that "owns" a method, or null if
* we know that its un-owned.
*/
private JSType getClassOfMethod(Node n, Node parent) {
if (parent.isAssign()) {
Node lValue = parent.getFirstChild();
if (NodeUtil.isGet(lValue)) {
// We have an assignment of the form "a.b = ...".
JSType lValueType = lValue.getJSType();
if (lValueType != null && lValueType.isNominalConstructor()) {
// If a.b is a constructor, then everything in this function
// belongs to the "a.b" type.
return (lValueType.toMaybeFunctionType()).getInstanceType();
} else {
// If a.b is not a constructor, then treat this as a method
// of whatever type is on "a".
return normalizeClassType(lValue.getFirstChild().getJSType());
}
} else {
// We have an assignment of the form "a = ...", so pull the
// type off the "a".
return normalizeClassType(lValue.getJSType());
}
} else if (NodeUtil.isFunctionDeclaration(n) ||
parent.isName()) {
return normalizeClassType(n.getJSType());
}
return null;
}
/**
* Normalize the type of a constructor, its instance, and its prototype
* all down to the same type (the instance type).
*/
private JSType normalizeClassType(JSType type) {
if (type == null || type.isUnknownType()) {
return type;
} else if (type.isNominalConstructor()) {
return (type.toMaybeFunctionType()).getInstanceType();
} else if (type.isFunctionPrototypeType()) {
FunctionType owner = ((ObjectType) type).getOwnerFunction();
if (owner.isConstructor()) {
return owner.getInstanceType();
}
}
return type;
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
checkNameDeprecation(t, n, parent);
checkNameVisibility(t, n, parent);
break;
case Token.GETPROP:
checkPropertyDeprecation(t, n, parent);
checkPropertyVisibility(t, n, parent);
checkConstantProperty(t, n);
break;
case Token.NEW:
checkConstructorDeprecation(t, n, parent);
break;
case Token.FUNCTION:
checkFinalClassOverrides(t, n, parent);
break;
}
}
/**
* Checks the given NEW node to ensure that access restrictions are obeyed.
*/
private void checkConstructorDeprecation(NodeTraversal t, Node n,
Node parent) {
JSType type = n.getJSType();
if (type != null) {
String deprecationInfo = getTypeDeprecationInfo(type);
if (deprecationInfo != null &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (!deprecationInfo.isEmpty()) {
compiler.report(
t.makeError(n, DEPRECATED_CLASS_REASON,
type.toString(), deprecationInfo));
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
/**
* An object type with declared template types, such as
* <code>Array.<string></code>.
*
*/
public final class TemplatizedType extends ProxyObjectType {
private static final long serialVersionUID = 1L;
final ImmutableList<JSType> templateTypes;
final TemplateTypeMapReplacer replacer;
TemplatizedType(
JSTypeRegistry registry, ObjectType objectType,
ImmutableList<JSType> templateTypes) {
super(registry, objectType, objectType.getTemplateTypeMap().addValues(
templateTypes));
// Cache which template keys were filled, and what JSTypes they were filled
// with.
ImmutableList<TemplateType> filledTemplateKeys =
objectType.getTemplateTypeMap().getUnfilledTemplateKeys();
ImmutableList.Builder<JSType> builder = ImmutableList.builder();
for (TemplateType filledTemplateKey : filledTemplateKeys) {
builder.add(getTemplateTypeMap().getTemplateType(filledTemplateKey));
}
this.templateTypes = builder.build();
replacer = new TemplateTypeMapReplacer(registry, getTemplateTypeMap());
}
@Override
String toStringHelper(boolean forAnnotations) {
String typeString = super.toStringHelper(forAnnotations);
if (!templateTypes.isEmpty()) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
typeString += ".<" + Joiner.on(",").join(templateTypes) + ">";
}
return typeString;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseTemplatizedType(this);
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseTemplatizedType(this, that);
}
@Override
public TemplatizedType toMaybeTemplatizedType() {
return this;
}
@Override
public ImmutableList<JSType> getTemplateTypes() {
return templateTypes;
}
@Override
public JSType getPropertyType(String propertyName) {
JSType result = super.getPropertyType(propertyName);
return result == null ? null : result.visit(replacer);
}
@Override
public boolean isSubtype(JSType that) {
return isSubtypeHelper(this, that);
}
boolean wrapsSameRawType(JSType that) {
return that.isTemplatizedType() && this.getReferencedTypeInternal()
.isEquivalentTo(
that.toMaybeTemplatizedType().getReferencedTypeInternal());
}
boolean wrapsRawType(JSType that) {
return this.getReferencedTypeInternal().isEquivalentTo(that);
}
/**
* Computes the greatest subtype of two related templatized types.
* @return The greatest subtype.
*/
JSType getGreatestSubtypeHelper(JSType rawThat) {
Preconditions.checkNotNull(rawThat);
if (!wrapsSameRawType(rawThat)) {
if (!rawThat.isTemplatizedType()) {
if (this.isSubtype(rawThat)) {
return this;
} else if (rawThat.isSubtype(this)) {
return filterNoResolvedType(rawThat);
}
}
if (this.isObject() && rawThat.isObject()) {
return this.getNativeType(JSTypeNative.NO_OBJECT_TYPE);
}
return this.getNativeType(JSTypeNative.NO_TYPE);
}
TemplatizedType that = rawThat.toMaybeTemplatizedType();
Preconditions.checkNotNull(that);
if (getTemplateTypeMap().checkEquivalenceHelper(
that.getTemplateTypeMap(), EquivalenceMethod.INVARIANT)) {
return this;
}
// For types that have the same raw type but different type parameters,
// we simply create a type has a "unknown" type parameter. This is
// equivalent to the raw type.
return getReferencedObjTypeInternal();
}
@Override
public TemplateTypeMap getTemplateTypeMap() {
return templateTypeMap;
}
@Override
public boolean hasAnyTemplateTypesInternal() {
return templateTypeMap.hasAnyTemplateTypesInternal();
}
/**
* @return The referenced ObjectType wrapped by this TemplatizedType.
*/
public ObjectType getReferencedType() {
return getReferencedObjTypeInternal();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> be used together.");
static final DiagnosticType CANNOT_USE_EXPORT_LOCALS_AND_EXTERN_PROP_REMOVAL =
DiagnosticType.error("JSC_CANNOT_USE_EXPORT_LOCALS_AND_EXTERN_PROP_REMOVAL",
"remove_unused_prototype_properties_in_externs " +
"and export_local_property_definitions cannot be used together.");
// Miscellaneous errors.
static final DiagnosticType REPORT_PATH_IO_ERROR =
DiagnosticType.error("JSC_REPORT_PATH_IO_ERROR",
"Error writing compiler report to {0}");
private static final DiagnosticType NAME_REF_GRAPH_FILE_ERROR =
DiagnosticType.error("JSC_NAME_REF_GRAPH_FILE_ERROR",
"Error \"{1}\" writing name reference graph to \"{0}\".");
private static final DiagnosticType NAME_REF_REPORT_FILE_ERROR =
DiagnosticType.error("JSC_NAME_REF_REPORT_FILE_ERROR",
"Error \"{1}\" writing name reference report to \"{0}\".");
private static final java.util.regex.Pattern GLOBAL_SYMBOL_NAMESPACE_PATTERN =
java.util.regex.Pattern.compile("^[a-zA-Z0-9$_]+$");
/**
* A global namespace to share across checking passes.
*/
private GlobalNamespace namespaceForChecks = null;
/**
* A symbol table for registering references that get removed during
* preprocessing.
*/
private PreprocessorSymbolTable preprocessorSymbolTable = null;
/**
* A type-tightener to share across optimization passes.
*/
private TightenTypes tightenTypes = null;
/** Names exported by goog.exportSymbol. */
private Set<String> exportedNames = null;
/** Shared name generator that remembers character encoding bias */
private NameGenerator nameGenerator = null;
/**
* Ids for cross-module method stubbing, so that each method has
* a unique id.
*/
private CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator =
new CrossModuleMethodMotion.IdGenerator();
/**
* Keys are arguments passed to getCssName() found during compilation; values
* are the number of times the key appeared as an argument to getCssName().
*/
private Map<String, Integer> cssNames = null;
/** The variable renaming map */
private VariableMap variableMap = null;
/** The property renaming map */
private VariableMap propertyMap = null;
/** The naming map for anonymous functions */
private VariableMap anonymousFunctionNameMap = null;
/** Fully qualified function names and globally unique ids */
private FunctionNames functionNames = null;
/** String replacement map */
private VariableMap stringMap = null;
/** Id generator map */
private String idGeneratorMap = null;
public DefaultPassConfig(CompilerOptions options) {
super(options);
}
@Override
protected State getIntermediateState() {
return new State(
cssNames == null ? null : Maps.newHashMap(cssNames),
exportedNames == null ? null :
Collections.unmodifiableSet(exportedNames),
crossModuleIdGenerator, variableMap, propertyMap,
anonymousFunctionNameMap, stringMap, functionNames, idGeneratorMap);
}
@Override
protected void setIntermediateState(State state) {
this.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>cssNames = state.cssNames == null ? null :
Maps.newHashMap(state.cssNames);
this.exportedNames = state.exportedNames == null ? null :
Sets.newHashSet(state.exportedNames);
this.crossModuleIdGenerator = state.crossModuleIdGenerator;
this.variableMap = state.variableMap;
this.propertyMap = state.propertyMap;
this.anonymousFunctionNameMap = state.anonymousFunctionNameMap;
this.stringMap = state.stringMap;
this.functionNames = state.functionNames;
this.idGeneratorMap = state.idGeneratorMap;
}
GlobalNamespace getGlobalNamespace() {
return namespaceForChecks;
}
PreprocessorSymbolTable getPreprocessorSymbolTable() {
return preprocessorSymbolTable;
}
private NameGenerator getNameGenerator() {
if (nameGenerator == null) {
nameGenerator = new NameGenerator(new HashSet<String>(0), "", null);
}
return nameGenerator;
}
void maybeInitializePreprocessorSymbolTable(AbstractCompiler compiler) {
if (options.ideMode) {
Node root = compiler.getRoot();
if (preprocessorSymbolTable == null ||
preprocessorSymbolTable.getRootNode() != root) {
preprocessorSymbolTable = new PreprocessorSymbolTable(root);
}
}
}
@Override
protected List<PassFactory> getChecks() {
List<PassFactory> checks = Lists.newArrayList();
checks.add(createEmptyPass("beforeStandardChecks"));
if (options.closurePass) {
checks.add(closureGoogScopeAliases);
checks.add(closureRewriteGoogClass);
}
if (options.nameAnonymousFunctionsOnly) {
if (options.anonymousFunctionNaming ==
AnonymousFunctionNamingPolicy.MAPPED) {
checks.add(nameMappedAnonymousFunctions);
} else if (options.anonymousFunctionNaming ==
AnonymousFunctionNamingPolicy.UNMAPPED) {
checks.add(nameUnmappedAnonymousFunctions);
}
return checks;
}
if (options.jqueryPass) {
checks.add(jqueryAliases);
}
if (options.angularPass) {
checks.add(angularPass);
}
checks.add(checkSideEffects);
if (options.checkSuspiciousCode ||
options.enables(DiagnosticGroups.GLOBAL_THIS) ||
options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) {
checks.add(suspiciousCode);
}
if (options.checkControlStructures
|| options.enables(DiagnosticGroups.ES5_STRICT)) {
checks.add(checkControlStructures);
}
if (options.checkRequires.isOn()) {
checks.add(checkRequires);
}
if (options.checkProvides.isOn()) {
checks.add(checkProvides);
}
// The following passes are more like "preprocessor" passes.
// It's important that they run before most checking passes.
// Perhaps this method should be renamed?
if (options.generateExports) {
checks.add(generateExports);
}
if (options.exportTestFunctions) {
checks.add(exportTestFunctions);
}
if (options.closurePass) {
checks.add(closurePrimitives);
}
if (options.closurePass && options
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.nameReferenceReportPath != null &&
!options.nameReferenceReportPath.isEmpty()) {
checks.add(printNameReferenceReport);
}
checks.add(createEmptyPass("afterStandardChecks"));
assertAllOneTimePasses(checks);
return checks;
}
@Override
protected List<PassFactory> getOptimizations() {
List<PassFactory> passes = Lists.newArrayList();
passes.add(garbageCollectChecks);
// TODO(nicksantos): The order of these passes makes no sense, and needs
// to be re-arranged.
if (options.instrumentForCoverage) {
passes.add(instrumentForCodeCoverage);
}
if (options.runtimeTypeCheck) {
passes.add(runtimeTypeCheck);
}
passes.add(createEmptyPass("beforeStandardOptimizations"));
if (options.replaceIdGenerators) {
passes.add(replaceIdGenerators);
}
// Optimizes references to the arguments variable.
if (options.optimizeArgumentsArray) {
passes.add(optimizeArgumentsArray);
}
// Abstract method removal works best on minimally modified code, and also
// only needs to run once.
if (options.closurePass &&
(options.removeAbstractMethods || options.removeClosureAsserts)) {
passes.add(closureCodeRemoval);
}
// Property disambiguation should only run once and needs to be done
// soon after type checking, both so that it can make use of type
// information and so that other passes can take advantage of the renamed
// properties.
if (options.disambiguatePrivateProperties) {
passes.add(disambiguatePrivateProperties);
}
// Collapsing properties can undo constant inlining, so we do this before
// the main optimization loop.
if (options.collapseProperties) {
passes.add(collapseProperties);
}
// ReplaceStrings runs after CollapseProperties in order to simplify
// pulling in values of constants defined in enums structures.
if (!options.replaceStringsFunctionDescriptions.isEmpty()) {
passes.add(replaceStrings);
}
// Tighten types based on actual usage.
if (options.tightenTypes) {
passes.add(tightenTypesBuilder);
}
// Property disambiguation should only run once and needs to be done
// soon after type checking, both so that it can make use of type
// information and so that other passes can take advantage of the renamed
// properties.
if (options.disambiguateProperties) {
passes.add(disambiguateProperties);
}
if (options.computeFunctionSideEffects) {
passes.add(markPureFunctions);
} else if (options.markNoSideEffectCalls) {
// TODO(user) The properties that this pass adds to CALL and NEW
// AST nodes increase the AST's in-memory size. Given that we are
// already running close to our memory limits, we could run into
// trouble if we end up using the @nosideeffects annotation a lot
// or compute @nosideeffects annotations by looking at function
// bodies. It should be easy to propagate @nosideeffects
// annotations as part of passes that depend on this property and
// store the result outside the AST (which would allow garbage
// collection once the pass is
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> (options.deadAssignmentElimination) {
passes.add(deadAssignmentsElimination);
}
if (!runOptimizeCalls) {
passes.add(removeUnusedVars);
}
}
if (runOptimizeCalls) {
passes.add(optimizeCallsAndRemoveUnusedVars);
}
assertAllLoopablePasses(passes);
return passes;
}
/** Creates several passes aimed at removing code. */
private List<PassFactory> getCodeRemovingPasses() {
List<PassFactory> passes = Lists.newArrayList();
if (options.collapseObjectLiterals && !isInliningForbidden()) {
passes.add(collapseObjectLiterals);
}
if (options.inlineVariables || options.inlineLocalVariables) {
passes.add(inlineVariables);
} else if (options.inlineConstantVars) {
passes.add(inlineConstants);
}
if (options.foldConstants) {
// These used to be one pass.
passes.add(minimizeExitPoints);
passes.add(peepholeOptimizations);
}
if (options.removeDeadCode) {
passes.add(removeUnreachableCode);
}
if (options.removeUnusedPrototypeProperties) {
passes.add(removeUnusedPrototypeProperties);
}
if (options.removeUnusedClassProperties && !isInliningForbidden()) {
passes.add(removeUnusedClassProperties);
}
assertAllLoopablePasses(passes);
return passes;
}
/**
* Checks for code that is probably wrong (such as stray expressions).
*/
final HotSwapPassFactory checkSideEffects =
new HotSwapPassFactory("checkSideEffects", true) {
@Override
protected HotSwapCompilerPass create(final AbstractCompiler compiler) {
// The current approach to protecting "hidden" side-effects is to
// wrap them in a function call that is stripped later, this shouldn't
// be done in IDE mode where AST changes may be unexpected.
boolean protectHiddenSideEffects =
options.protectHiddenSideEffects && !options.ideMode;
return new CheckSideEffects(compiler,
options.checkSuspiciousCode ? CheckLevel.WARNING : CheckLevel.OFF,
protectHiddenSideEffects);
}
};
/**
* Checks for code that is probably wrong (such as stray expressions).
*/
final PassFactory stripSideEffectProtection =
new PassFactory("stripSideEffectProtection", true) {
@Override
protected CompilerPass create(final AbstractCompiler
compiler) {
return new CheckSideEffects.StripProtection(compiler);
}
};
/**
* Checks for code that is probably wrong (such as stray expressions).
*/
final HotSwapPassFactory suspiciousCode =
new HotSwapPassFactory("suspiciousCode", true) {
@Override
protected HotSwapCompilerPass create(final AbstractCompiler compiler) {
List<Callback> sharedCallbacks = Lists.newArrayList();
if (options.checkSuspiciousCode) {
sharedCallbacks.add(new CheckSuspiciousCode());
}
if (options.enables(DiagnosticGroups.GLOBAL_THIS)) {
sharedCallbacks.add(new CheckGlobalThis(compiler));
}
if (options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) {
sharedCallbacks.add(new CheckDebuggerStatement(compiler));
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> }
return combineChecks(compiler, sharedCallbacks);
}
};
/** Verify that all the passes are one-time passes. */
private static void assertAllOneTimePasses(List<PassFactory> passes) {
for (PassFactory pass : passes) {
Preconditions.checkState(pass.isOneTimePass());
}
}
/** Verify that all the passes are multi-run passes. */
private static void assertAllLoopablePasses(List<PassFactory> passes) {
for (PassFactory pass : passes) {
Preconditions.checkState(!pass.isOneTimePass());
}
}
/** Checks for validity of the control structures. */
final HotSwapPassFactory checkControlStructures =
new HotSwapPassFactory("checkControlStructures", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new ControlStructureCheck(compiler);
}
};
/** Checks that all constructed classes are goog.require()d. */
final HotSwapPassFactory checkRequires =
new HotSwapPassFactory("checkRequires", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new CheckRequiresForConstructors(compiler, options.checkRequires);
}
};
/** Makes sure @constructor is paired with goog.provides(). */
final HotSwapPassFactory checkProvides =
new HotSwapPassFactory("checkProvides", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new CheckProvides(compiler, options.checkProvides);
}
};
private static final DiagnosticType GENERATE_EXPORTS_ERROR =
DiagnosticType.error(
"JSC_GENERATE_EXPORTS_ERROR",
"Exports can only be generated if export symbol/property " +
"functions are set.");
/** Generates exports for @export annotations. */
final PassFactory generateExports = new PassFactory("generateExports", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
if (options.removeUnusedPrototypePropertiesInExterns
&& options.exportLocalPropertyDefinitions) {
return new ErrorPass(
compiler, CANNOT_USE_EXPORT_LOCALS_AND_EXTERN_PROP_REMOVAL);
}
CodingConvention convention = compiler.getCodingConvention();
if (convention.getExportSymbolFunction() != null &&
convention.getExportPropertyFunction() != null) {
return new GenerateExports(compiler,
options.exportLocalPropertyDefinitions,
convention.getExportSymbolFunction(),
convention.getExportPropertyFunction());
} else {
return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR);
}
}
};
/** Generates exports for functions associated with JsUnit. */
final PassFactory exportTestFunctions =
new PassFactory("exportTestFunctions", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
CodingConvention convention = compiler.getCodingConvention();
if (convention.getExportSymbolFunction() != null) {
return new ExportTestFunctions(compiler,
convention.getExportSymbolFunction(),
convention.getExportPropertyFunction());
} else {
return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR);
}
}
};
/** Raw exports processing pass. */
final PassFactory gatherRawExports =
new PassFactory("
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>gatherRawExports", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
final GatherRawExports pass = new GatherRawExports(
compiler);
return new CompilerPass() {
@Override
public void process(Node externs, Node root) {
pass.process(externs, root);
if (exportedNames == null) {
exportedNames = Sets.newHashSet();
}
exportedNames.addAll(pass.getExportedVariableNames());
}
};
}
};
/** Closure pre-processing pass. */
@SuppressWarnings("deprecation")
final HotSwapPassFactory closurePrimitives =
new HotSwapPassFactory("closurePrimitives", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
maybeInitializePreprocessorSymbolTable(compiler);
final ProcessClosurePrimitives pass = new ProcessClosurePrimitives(
compiler,
preprocessorSymbolTable,
options.brokenClosureRequiresLevel);
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
pass.process(externs, root);
exportedNames = pass.getExportedVariableNames();
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
pass.hotSwapScript(scriptRoot, originalRoot);
}
};
}
};
/** Expand jQuery Primitives and Aliases pass. */
final PassFactory jqueryAliases = new PassFactory("jqueryAliases", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new ExpandJqueryAliases(compiler);
}
};
/** Process AngularJS-specific annotations. */
final PassFactory angularPass =
new PassFactory("angularPass", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new AngularPass(compiler);
}
};
/**
* The default i18n pass.
* A lot of the options are not configurable, because ReplaceMessages
* has a lot of legacy logic.
*/
final PassFactory replaceMessages = new PassFactory("replaceMessages", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new ReplaceMessages(compiler,
options.messageBundle,
/* warn about message dupes */
true,
/* allow messages with goog.getMsg */
JsMessage.Style.getFromParams(true, false),
/* if we can't find a translation, don't worry about it. */
false);
}
};
final PassFactory replaceMessagesForChrome =
new PassFactory("replaceMessages", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new ReplaceMessagesForChrome(compiler,
new GoogleJsMessageIdGenerator(options.tcProjectId),
/* warn about message dupes */
true,
/* allow messages with goog.getMsg */
JsMessage.Style.getFromParams(true, false));
}
};
/** Applies aliases and inlines goog.scope. */
final HotSwapPassFactory closureGoogScopeAliases =
new HotSwapPassFactory("closureGoogScopeAliases", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
maybeInitializePreprocessorSymbolTable(compiler);
return new ScopedAliases(
compiler,
preprocessorSymbolTable,
options.getAliasTransformation
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Handler());
}
};
/** Rewrites goog.class */
final HotSwapPassFactory closureRewriteGoogClass =
new HotSwapPassFactory("closureRewriteGoogClass", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new ClosureRewriteClass(compiler);
}
};
/** Checks that CSS class names are wrapped in goog.getCssName */
final PassFactory closureCheckGetCssName =
new PassFactory("closureCheckGetCssName", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
String blacklist = options.checkMissingGetCssNameBlacklist;
Preconditions.checkState(blacklist != null && !blacklist.isEmpty(),
"Not checking use of goog.getCssName because of empty blacklist.");
return new CheckMissingGetCssName(
compiler, options.checkMissingGetCssNameLevel, blacklist);
}
};
/**
* Processes goog.getCssName. The cssRenamingMap is used to lookup
* replacement values for the classnames. If null, the raw class names are
* inlined.
*/
final PassFactory closureReplaceGetCssName =
new PassFactory("closureReplaceGetCssName", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
Map<String, Integer> newCssNames = null;
if (options.gatherCssNames) {
newCssNames = Maps.newHashMap();
}
ReplaceCssNames pass = new ReplaceCssNames(
compiler,
newCssNames,
options.cssRenamingWhitelist);
pass.process(externs, jsRoot);
cssNames = newCssNames;
}
};
}
};
/**
* Creates synthetic blocks to prevent FoldConstants from moving code
* past markers in the source.
*/
final PassFactory createSyntheticBlocks =
new PassFactory("createSyntheticBlocks", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new CreateSyntheticBlocks(compiler,
options.syntheticBlockStartMarker,
options.syntheticBlockEndMarker);
}
};
/** Various peephole optimizations. */
final PassFactory peepholeOptimizations =
new PassFactory("peepholeOptimizations", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
final boolean late = false;
return new PeepholeOptimizationsPass(compiler,
new PeepholeMinimizeConditions(late),
new PeepholeSubstituteAlternateSyntax(late),
new PeepholeReplaceKnownMethods(late),
new PeepholeRemoveDeadCode(),
new PeepholeFoldConstants(late),
new PeepholeCollectPropertyAssignments());
}
};
/** Same as peepholeOptimizations but aggressively merges code together */
final PassFactory latePeepholeOptimizations =
new PassFactory("latePeepholeOptimizations", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
final boolean late = true;
return new PeepholeOptimizationsPass(compiler,
new StatementFusion(options.aggressiveFusion),
new P
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>eepholeRemoveDeadCode(),
new PeepholeMinimizeConditions(late),
new PeepholeSubstituteAlternateSyntax(late),
new PeepholeReplaceKnownMethods(late),
new PeepholeFoldConstants(late),
new ReorderConstantExpression());
}
};
/** Checks that all variables are defined. */
final HotSwapPassFactory checkVars =
new HotSwapPassFactory("checkVars", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new VarCheck(compiler);
}
};
/** Checks for RegExp references. */
final PassFactory checkRegExp =
new PassFactory("checkRegExp", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
final CheckRegExp pass = new CheckRegExp(compiler);
return new CompilerPass() {
@Override
public void process(Node externs, Node root) {
pass.process(externs, root);
compiler.setHasRegExpGlobalReferences(
pass.isGlobalRegExpPropertiesUsed());
}
};
}
};
/** Checks that references to variables look reasonable. */
final HotSwapPassFactory checkVariableReferences =
new HotSwapPassFactory("checkVariableReferences", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new VariableReferenceCheck(
compiler, options.aggressiveVarCheck);
}
};
/** Pre-process goog.testing.ObjectPropertyString. */
final PassFactory objectPropertyStringPreprocess =
new PassFactory("ObjectPropertyStringPreprocess", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new ObjectPropertyStringPreprocess(compiler);
}
};
/** Creates a typed scope and adds types to the type registry. */
final HotSwapPassFactory resolveTypes =
new HotSwapPassFactory("resolveTypes", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new GlobalTypeResolver(compiler);
}
};
/** Clears the typed scope when we're done. */
final PassFactory clearTypedScopePass =
new PassFactory("clearTypedScopePass", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new ClearTypedScope();
}
};
/** Runs type inference. */
final HotSwapPassFactory inferTypes =
new HotSwapPassFactory("inferTypes", true) {
@Override
protected HotSwapCompilerPass create(final AbstractCompiler compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
Preconditions.checkNotNull(topScope);
Preconditions.checkNotNull(getTypedScopeCreator());
makeTypeInference(compiler).process(externs, root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
makeTypeInference(compiler).inferAllScopes(scriptRoot);
}
};
}
};
final HotSwapPassFactory inferJsDocInfo =
new HotSwapPassFactory("inferJsDocInfo", true) {
@Override
protected HotSwapCompilerPass create(final AbstractCompiler compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
Preconditions.checkNotNull(topScope);
Preconditions.checkNotNull(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>getTypedScopeCreator());
makeInferJsDocInfo(compiler).process(externs, root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
makeInferJsDocInfo(compiler).hotSwapScript(scriptRoot, originalRoot);
}
};
}
};
/** Checks type usage */
final HotSwapPassFactory checkTypes =
new HotSwapPassFactory("checkTypes", true) {
@Override
protected HotSwapCompilerPass create(final AbstractCompiler compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
Preconditions.checkNotNull(topScope);
Preconditions.checkNotNull(getTypedScopeCreator());
TypeCheck check = makeTypeCheck(compiler);
check.process(externs, root);
compiler.getErrorManager().setTypedPercent(check.getTypedPercent());
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
makeTypeCheck(compiler).check(scriptRoot, false);
}
};
}
};
/**
* Checks possible execution paths of the program for problems: missing return
* statements and dead code.
*/
final HotSwapPassFactory checkControlFlow =
new HotSwapPassFactory("checkControlFlow", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
List<Callback> callbacks = Lists.newArrayList();
if (options.checkUnreachableCode.isOn()) {
callbacks.add(
new CheckUnreachableCode(compiler, options.checkUnreachableCode));
}
if (options.checkMissingReturn.isOn() && options.checkTypes) {
callbacks.add(
new CheckMissingReturn(compiler, options.checkMissingReturn));
}
return combineChecks(compiler, callbacks);
}
};
/** Checks access controls. Depends on type-inference. */
final HotSwapPassFactory checkAccessControls =
new HotSwapPassFactory("checkAccessControls", true) {
@Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new CheckAccessControls(compiler);
}
};
/** Executes the given callbacks with a {@link CombinedCompilerPass}. */
private static HotSwapCompilerPass combineChecks(AbstractCompiler compiler,
List<Callback> callbacks) {
Preconditions.checkArgument(callbacks.size() > 0);
Callback[] array = callbacks.toArray(new Callback[callbacks.size()]);
return new CombinedCompilerPass(compiler, array);
}
/** A compiler pass that resolves types in the global scope. */
class GlobalTypeResolver implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
GlobalTypeResolver(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
if (topScope == null) {
regenerateGlobalTypedScope(compiler, root.getParent());
} else {
compiler.getTypeRegistry().resolveTypesInScope(topScope);
}
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
patchGlobalTypedScope(compiler, scriptRoot);
}
}
/** A compiler pass that clears the global scope. */
class ClearTypedScope implements CompilerPass {
@Override
public void process(Node
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> externs, Node root) {
clearTypedScope();
}
}
/** Checks global name usage. */
final PassFactory checkGlobalNames =
new PassFactory("checkGlobalNames", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
// Create a global namespace for analysis by check passes.
// Note that this class does all heavy computation lazily,
// so it's OK to create it here.
namespaceForChecks = new GlobalNamespace(compiler, externs, jsRoot);
new CheckGlobalNames(compiler, options.checkGlobalNamesLevel)
.injectNamespace(namespaceForChecks).process(externs, jsRoot);
}
};
}
};
/** Checks that the code is ES5 or Caja compliant. */
final PassFactory checkStrictMode =
new PassFactory("checkStrictMode", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new StrictModeCheck(compiler,
!options.checkSymbols, // don't check variables twice
!options.checkCaja); // disable eval check if not Caja
}
};
/** Process goog.tweak.getTweak() calls. */
final PassFactory processTweaks = new PassFactory("processTweaks", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
new ProcessTweaks(compiler,
options.getTweakProcessing().shouldStrip(),
options.getTweakReplacements()).process(externs, jsRoot);
}
};
}
};
/** Override @define-annotated constants. */
final PassFactory processDefines = new PassFactory("processDefines", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
Map<String, Node> replacements = getAdditionalReplacements(options);
replacements.putAll(options.getDefineReplacements());
new ProcessDefines(compiler, replacements)
.injectNamespace(namespaceForChecks).process(externs, jsRoot);
}
};
}
};
/** Release references to data that is only needed during checks. */
final PassFactory garbageCollectChecks =
new HotSwapPassFactory("garbageCollectChecks", true) {
@Override
protected HotSwapCompilerPass create(final AbstractCompiler compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
// Kill the global namespace so that it can be garbage collected
// after all passes are through with it.
namespaceForChecks = null;
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
process(null, null);
}
};
}
};
/** Checks that all constants are not modified */
final PassFactory checkConsts = new PassFactory("checkConsts", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new ConstCheck(compiler);
}
};
/** Check memory bloat patterns */
final PassFactory check
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>EventfulObjectDisposal =
new PassFactory("checkEventfulObjectDisposal", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new CheckEventfulObjectDisposal(compiler,
options.checkEventfulObjectDisposalPolicy);
}
};
/** Computes the names of functions for later analysis. */
final PassFactory computeFunctionNames =
new PassFactory("computeFunctionNames", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return ((functionNames = new FunctionNames(compiler)));
}
};
/** Skips Caja-private properties in for-in loops */
final PassFactory ignoreCajaProperties =
new PassFactory("ignoreCajaProperties", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new IgnoreCajaProperties(compiler);
}
};
/** Inserts run-time type assertions for debugging. */
final PassFactory runtimeTypeCheck =
new PassFactory("runtimeTypeCheck", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new RuntimeTypeCheck(compiler,
options.runtimeTypeCheckLogFunction);
}
};
/** Generates unique ids. */
final PassFactory replaceIdGenerators =
new PassFactory("replaceIdGenerators", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
ReplaceIdGenerators pass =
new ReplaceIdGenerators(
compiler, options.idGenerators, options.generatePseudoNames,
options.idGeneratorsMapSerialized);
pass.process(externs, root);
idGeneratorMap = pass.getSerializedIdMappings();
}
};
}
};
/** Replace strings. */
final PassFactory replaceStrings = new PassFactory("replaceStrings", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
ReplaceStrings pass = new ReplaceStrings(
compiler,
options.replaceStringsPlaceholderToken,
options.replaceStringsFunctionDescriptions,
options.replaceStringsReservedStrings,
options.replaceStringsInputMap);
pass.process(externs, root);
stringMap = pass.getStringMap();
}
};
}
};
/** Optimizes the "arguments" array. */
final PassFactory optimizeArgumentsArray =
new PassFactory("optimizeArgumentsArray", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new OptimizeArgumentsArray(compiler);
}
};
/** Remove variables set to goog.abstractMethod. */
final PassFactory closureCodeRemoval =
new PassFactory("closureCodeRemoval", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new ClosureCodeRemoval(compiler, options.removeAbstractMethods,
options.removeClosureAsserts);
}
};
/** Special case optimizations for closure functions. */
final PassFactory closureOptimizePrimitives =
new PassFactory("closureOptimizePrimitives", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new ClosureOptimizePrimitives(compiler);
}
};
/** Puts global symbols into a single object. */
final PassFactory rescopeGlobalSymbols =
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
new PassFactory("rescopeGlobalSymbols", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new RescopeGlobalSymbols(
compiler,
options.renamePrefixNamespace,
options.renamePrefixNamespaceAssumeCrossModuleNames);
}
};
/** Collapses names in the global scope. */
final PassFactory collapseProperties =
new PassFactory("collapseProperties", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new CollapseProperties(
compiler, options.collapsePropertiesOnExternTypes,
!isInliningForbidden());
}
};
/** Rewrite properties as variables. */
final PassFactory collapseObjectLiterals =
new PassFactory("collapseObjectLiterals", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new InlineObjectLiterals(
compiler, compiler.getUniqueNameIdSupplier());
}
};
/**
* Try to infer the actual types, which may be narrower
* than the declared types.
*/
final PassFactory tightenTypesBuilder =
new PassFactory("tightenTypes", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
if (!options.checkTypes) {
return new ErrorPass(compiler, TIGHTEN_TYPES_WITHOUT_TYPE_CHECK);
}
tightenTypes = new TightenTypes(compiler);
return tightenTypes;
}
};
/** Disambiguate property names based on the coding convention. */
final PassFactory disambiguatePrivateProperties =
new PassFactory("disambiguatePrivateProperties", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new DisambiguatePrivateProperties(compiler);
}
};
/** Disambiguate property names based on type information. */
final PassFactory disambiguateProperties =
new PassFactory("disambiguateProperties", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
if (tightenTypes == null) {
return DisambiguateProperties.forJSTypeSystem(compiler,
options.propertyInvalidationErrors);
} else {
return DisambiguateProperties.forConcreteTypeSystem(
compiler, tightenTypes, options.propertyInvalidationErrors);
}
}
};
/**
* Chain calls to functions that return this.
*/
final PassFactory chainCalls = new PassFactory("chainCalls", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new ChainCalls(compiler);
}
};
/**
* Rewrite instance methods as static methods, to make them easier
* to inline.
*/
final PassFactory devirtualizePrototypeMethods =
new PassFactory("devirtualizePrototypeMethods", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new DevirtualizePrototypeMethods(compiler);
}
};
/**
* Optimizes unused function arguments, unused return values, and inlines
* constant parameters. Also runs RemoveUnusedVars.
*/
final PassFactory optimizeCallsAndRemoveUnusedVars =
new PassFactory("optimizeCalls_and_removeUnusedVars", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
OptimizeCalls passes = new OptimizeCalls(compiler);
if (
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>options.optimizeReturns) {
// Remove unused return values.
passes.addPass(new OptimizeReturns(compiler));
}
if (options.optimizeParameters) {
// Remove all parameters that are constants or unused.
passes.addPass(new OptimizeParameters(compiler));
}
if (options.optimizeCalls) {
boolean removeOnlyLocals = options.removeUnusedLocalVars
&& !options.removeUnusedVars;
boolean preserveAnonymousFunctionNames =
options.anonymousFunctionNaming !=
AnonymousFunctionNamingPolicy.OFF;
passes.addPass(
new RemoveUnusedVars(compiler, !removeOnlyLocals,
preserveAnonymousFunctionNames, true));
}
return passes;
}
};
/**
* Look for function calls that are pure, and annotate them
* that way.
*/
final PassFactory markPureFunctions =
new PassFactory("markPureFunctions", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new PureFunctionIdentifier.Driver(
compiler, options.debugFunctionSideEffectsPath, false);
}
};
/**
* Look for function calls that have no side effects, and annotate them
* that way.
*/
final PassFactory markNoSideEffectCalls =
new PassFactory("markNoSideEffectCalls", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new MarkNoSideEffectCalls(compiler);
}
};
/** Inlines variables heuristically. */
final PassFactory inlineVariables =
new PassFactory("inlineVariables", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
if (isInliningForbidden()) {
// In old renaming schemes, inlining a variable can change whether
// or not a property is renamed. This is bad, and those old renaming
// schemes need to die.
return new ErrorPass(compiler, CANNOT_USE_PROTOTYPE_AND_VAR);
} else {
InlineVariables.Mode mode;
if (options.inlineVariables) {
mode = InlineVariables.Mode.ALL;
} else if (options.inlineLocalVariables) {
mode = InlineVariables.Mode.LOCALS_ONLY;
} else {
throw new IllegalStateException("No variable inlining option set.");
}
return new InlineVariables(compiler, mode, true);
}
}
};
/** Inlines variables that are marked as constants. */
final PassFactory inlineConstants =
new PassFactory("inlineConstants", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new InlineVariables(
compiler, InlineVariables.Mode.CONSTANTS_ONLY, true);
}
};
/**
* Perform local control flow optimizations.
*/
final PassFactory minimizeExitPoints =
new PassFactory("minimizeExitPoints", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new MinimizeExitPoints(compiler);
}
};
/**
* Use data flow analysis to remove dead branches.
*/
final PassFactory removeUnreachableCode =
new PassFactory("removeUnreachableCode", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new UnreachableCodeElimination(compiler, true);
}
};
/**
* Remove prototype properties that do not appear to be used.
*/
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
final PassFactory removeUnusedPrototypeProperties =
new PassFactory("removeUnusedPrototypeProperties", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new RemoveUnusedPrototypeProperties(
compiler, options.removeUnusedPrototypePropertiesInExterns,
!options.removeUnusedVars);
}
};
/**
* Remove prototype properties that do not appear to be used.
*/
final PassFactory removeUnusedClassProperties =
new PassFactory("removeUnusedClassProperties", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new RemoveUnusedClassProperties(compiler);
}
};
/**
* Process smart name processing - removes unused classes and does referencing
* starting with minimum set of names.
*/
final PassFactory smartNamePass = new PassFactory("smartNamePass", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node root) {
NameAnalyzer na = new NameAnalyzer(compiler, false);
na.process(externs, root);
String reportPath = options.reportPath;
if (reportPath != null) {
try {
Files.write(na.getHtmlReport(), new File(reportPath),
Charsets.UTF_8);
} catch (IOException e) {
compiler.report(JSError.make(REPORT_PATH_IO_ERROR, reportPath));
}
}
if (options.smartNameRemoval) {
na.removeUnreferenced();
}
}
};
}
};
/**
* Process smart name processing - removes unused classes and does referencing
* starting with minimum set of names.
*/
final PassFactory smartNamePass2 = new PassFactory("smartNamePass", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node root) {
NameAnalyzer na = new NameAnalyzer(compiler, false);
na.process(externs, root);
na.removeUnreferenced();
}
};
}
};
/** Inlines simple methods, like getters */
final PassFactory inlineSimpleMethods =
new PassFactory("inlineSimpleMethods", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new InlineSimpleMethods(compiler);
}
};
/** Kills dead assignments. */
final PassFactory deadAssignmentsElimination =
new PassFactory("deadAssignmentsElimination", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new DeadAssignmentsElimination(compiler);
}
};
/** Inlines function calls. */
final PassFactory inlineFunctions =
new PassFactory("inlineFunctions", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
boolean enableBlockInlining = !isInliningForbidden();
return new InlineFunctions(
compiler,
compiler.getUniqueNameIdSupplier(),
options.inlineFunctions,
options.inlineLocalFunctions,
enableBlockInlining,
options.assumeStrictThis()
|| options.getLanguageIn() == LanguageMode.ECMASCRIPT5_STRICT,
options.assumeClosuresOnlyCaptureReferences);
}
};
/** Inlines constant properties. */
final PassFactory
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> inlineProperties =
new PassFactory("inlineProperties", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new InlineProperties(compiler);
}
};
/** Removes variables that are never used. */
final PassFactory removeUnusedVars =
new PassFactory("removeUnusedVars", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
boolean removeOnlyLocals = options.removeUnusedLocalVars
&& !options.removeUnusedVars;
boolean preserveAnonymousFunctionNames =
options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF;
return new RemoveUnusedVars(
compiler,
!removeOnlyLocals,
preserveAnonymousFunctionNames,
false);
}
};
/**
* Move global symbols to a deeper common module
*/
final PassFactory crossModuleCodeMotion =
new PassFactory("crossModuleCodeMotion", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new CrossModuleCodeMotion(compiler, compiler.getModuleGraph());
}
};
/**
* Move methods to a deeper common module
*/
final PassFactory crossModuleMethodMotion =
new PassFactory("crossModuleMethodMotion", false) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new CrossModuleMethodMotion(
compiler, crossModuleIdGenerator,
// Only move properties in externs if we're not treating
// them as exports.
options.removeUnusedPrototypePropertiesInExterns);
}
};
/**
* Specialize the initial module at the cost of later modules
*/
final PassFactory specializeInitialModule =
new PassFactory("specializeInitialModule", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new SpecializeModule(compiler, devirtualizePrototypeMethods,
inlineFunctions, removeUnusedPrototypeProperties);
}
};
/** A data-flow based variable inliner. */
final PassFactory flowSensitiveInlineVariables =
new PassFactory("flowSensitiveInlineVariables", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new FlowSensitiveInlineVariables(compiler);
}
};
/** Uses register-allocation algorithms to use fewer variables. */
final PassFactory coalesceVariableNames =
new PassFactory("coalesceVariableNames", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new CoalesceVariableNames(compiler, options.generatePseudoNames);
}
};
/**
* Some simple, local collapses (e.g., {@code var x; var y;} becomes
* {@code var x,y;}.
*/
final PassFactory exploitAssign = new PassFactory("exploitAssign", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new PeepholeOptimizationsPass(compiler,
new ExploitAssigns());
}
};
/**
* Some simple, local collapses (e.g., {@code var x; var y;} becomes
* {@code var x,y;}.
*/
final PassFactory collapseVariableDeclarations =
new PassFactory("collapseVariableDeclarations", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new CollapseVariableDeclarations(compiler);
}
};
/**
* Simple global collapses of
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> variable declarations.
*/
final PassFactory groupVariableDeclarations =
new PassFactory("groupVariableDeclarations", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new GroupVariableDeclarations(compiler);
}
};
/**
* Extracts common sub-expressions.
*/
final PassFactory extractPrototypeMemberDeclarations =
new PassFactory("extractPrototypeMemberDeclarations", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new ExtractPrototypeMemberDeclarations(
compiler, Pattern.USE_GLOBAL_TEMP);
}
};
/** Rewrites common function definitions to be more compact. */
final PassFactory rewriteFunctionExpressions =
new PassFactory("rewriteFunctionExpressions", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new FunctionRewriter(compiler);
}
};
/** Collapses functions to not use the VAR keyword. */
final PassFactory collapseAnonymousFunctions =
new PassFactory("collapseAnonymousFunctions", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new CollapseAnonymousFunctions(compiler);
}
};
/** Moves function declarations to the top, to simulate actual hoisting. */
final PassFactory moveFunctionDeclarations =
new PassFactory("moveFunctionDeclarations", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new MoveFunctionDeclarations(compiler);
}
};
final PassFactory nameUnmappedAnonymousFunctions =
new PassFactory("nameAnonymousFunctions", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new NameAnonymousFunctions(compiler);
}
};
final PassFactory nameMappedAnonymousFunctions =
new PassFactory("nameAnonymousFunctions", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
NameAnonymousFunctionsMapped naf =
new NameAnonymousFunctionsMapped(
compiler, options.inputAnonymousFunctionNamingMap);
naf.process(externs, root);
anonymousFunctionNameMap = naf.getFunctionMap();
}
};
}
};
/** Alias external symbols. */
final PassFactory aliasExternals = new PassFactory("aliasExternals", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new AliasExternals(compiler, compiler.getModuleGraph(),
options.unaliasableGlobals, options.aliasableGlobals);
}
};
/**
* Alias string literals with global variables, to avoid creating lots of
* transient objects.
*/
final PassFactory aliasStrings = new PassFactory("aliasStrings", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new AliasStrings(
compiler,
compiler.getModuleGraph(),
options.aliasAllStrings ? null : options.aliasableStrings,
options.aliasStringsBlacklist,
options.outputJsStringUsage);
}
};
/** Aliases common keywords (true, false) */
final PassFactory aliasKeywords = new PassFactory("aliasKeywords", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new AliasKeywords(compiler);
}
};
/** Handling for the ObjectPropertyString primitive. */
final PassFactory objectPropertyStringPostprocess =
new
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> PassFactory("ObjectPropertyStringPostprocess", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new ObjectPropertyStringPostprocess(compiler);
}
};
/**
* Renames properties so that the two properties that never appear on
* the same object get the same name.
*/
final PassFactory ambiguateProperties =
new PassFactory("ambiguateProperties", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new AmbiguateProperties(
compiler, options.anonymousFunctionNaming.getReservedCharacters());
}
};
/**
* Mark the point at which the normalized AST assumptions no longer hold.
*/
final PassFactory markUnnormalized =
new PassFactory("markUnnormalized", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
compiler.setLifeCycleStage(LifeCycleStage.RAW);
}
};
}
};
/** Denormalize the AST for code generation. */
final PassFactory denormalize = new PassFactory("denormalize", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new Denormalize(compiler);
}
};
/** Inverting name normalization. */
final PassFactory invertContextualRenaming =
new PassFactory("invertContextualRenaming", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return MakeDeclaredNamesUnique.getContextualRenameInverter(compiler);
}
};
/**
* Renames properties.
*/
final PassFactory renameProperties =
new PassFactory("renameProperties", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
final VariableMap prevPropertyMap = options.inputPropertyMap;
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
propertyMap = runPropertyRenaming(
compiler, prevPropertyMap, externs, root);
}
};
}
};
private VariableMap runPropertyRenaming(
AbstractCompiler compiler, VariableMap prevPropertyMap,
Node externs, Node root) {
char[] reservedChars =
options.anonymousFunctionNaming.getReservedCharacters();
switch (options.propertyRenaming) {
case HEURISTIC:
RenamePrototypes rproto = new RenamePrototypes(compiler, false,
reservedChars, prevPropertyMap);
rproto.process(externs, root);
return rproto.getPropertyMap();
case AGGRESSIVE_HEURISTIC:
RenamePrototypes rproto2 = new RenamePrototypes(compiler, true,
reservedChars, prevPropertyMap);
rproto2.process(externs, root);
return rproto2.getPropertyMap();
case ALL_UNQUOTED:
RenameProperties rprop = new RenameProperties(
compiler, options.propertyAffinity, options.generatePseudoNames,
prevPropertyMap, reservedChars);
rprop.process(externs, root);
return rprop.getPropertyMap();
default:
throw new IllegalStateException(
"Unrecognized property renaming policy");
}
}
/** Renames variables. */
final PassFactory renameVars = new PassFactory("renameVars", true) {
@Override
protected Compiler
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Pass create(final AbstractCompiler compiler) {
final VariableMap prevVariableMap = options.inputVariableMap;
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
variableMap = runVariableRenaming(
compiler, prevVariableMap, externs, root);
}
};
}
};
private VariableMap runVariableRenaming(
AbstractCompiler compiler, VariableMap prevVariableMap,
Node externs, Node root) {
char[] reservedChars =
options.anonymousFunctionNaming.getReservedCharacters();
boolean preserveAnonymousFunctionNames =
options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF;
Set<String> reservedNames = Sets.newHashSet();
if (options.renamePrefixNamespace != null) {
// don't use the prefix name as a global symbol.
reservedNames.add(options.renamePrefixNamespace);
}
if (exportedNames != null) {
reservedNames.addAll(exportedNames);
}
reservedNames.addAll(ParserRunner.getReservedVars());
if (options.alternateRenaming) {
RenameVars2 rn = new RenameVars2(
compiler,
options.renamePrefix,
options.variableRenaming == VariableRenamingPolicy.LOCAL,
preserveAnonymousFunctionNames,
options.generatePseudoNames,
options.shadowVariables,
prevVariableMap,
reservedChars,
reservedNames);
rn.process(externs, root);
return rn.getVariableMap();
}
RenameVars rn = new RenameVars(
compiler,
options.renamePrefix,
options.variableRenaming == VariableRenamingPolicy.LOCAL,
preserveAnonymousFunctionNames,
options.generatePseudoNames,
options.shadowVariables,
prevVariableMap,
reservedChars,
reservedNames);
rn.process(externs, root);
return rn.getVariableMap();
}
/** Renames labels */
final PassFactory gatherCharBias = new PassFactory("gatherCharBias", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new GatherCharacterEncodingBias(
compiler,
getNameGenerator(),
options.variableRenaming != VariableRenamingPolicy.LOCAL);
}
};
/** Renames labels */
final PassFactory renameLabels = new PassFactory("renameLabels", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
if (options.aggressiveRenaming) {
return new RenameLabels(compiler, getNameGenerator());
} else {
return new RenameLabels(compiler);
}
}
};
/** Convert bracket access to dot access */
final PassFactory convertToDottedProperties =
new PassFactory("convertToDottedProperties", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new ConvertToDottedProperties(compiler);
}
};
/** Checks that all variables are defined. */
final PassFactory sanityCheckAst = new PassFactory("sanityCheckAst", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new AstValidator();
}
};
/** Checks that all variables are defined. */
final PassFactory sanityCheckVars = new PassFactory("sanityCheckVars", true) {
@Override
protected CompilerPass create(AbstractCompiler compiler) {
return new VarCheck(compiler, true);
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> };
/** Adds instrumentations according to an instrumentation template. */
final PassFactory instrumentFunctions =
new PassFactory("instrumentFunctions", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
try {
FileReader templateFile =
new FileReader(options.instrumentationTemplate);
(new InstrumentFunctions(
compiler, functionNames,
options.instrumentationTemplate,
options.appNameStr,
templateFile)).process(externs, root);
} catch (IOException e) {
compiler.report(
JSError.make(AbstractCompiler.READ_ERROR,
options.instrumentationTemplate));
}
}
};
}
};
/** Adds instrumentation for memory allocations. */
final PassFactory instrumentMemoryAllocations =
new PassFactory("instrumentMemoryAllocations", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new InstrumentMemoryAllocPass(compiler);
}
};
final PassFactory instrumentForCodeCoverage =
new PassFactory("instrumentForCodeCoverage", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
// TODO(johnlenz): make global instrumentation an option
return new CoverageInstrumentationPass(
compiler, CoverageReach.CONDITIONAL);
}
};
/**
* Create a no-op pass that can only run once. Used to break up loops.
*/
static PassFactory createEmptyPass(String name) {
return new PassFactory(name, true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return runInSerial();
}
};
}
/**
* Runs custom passes that are designated to run at a particular time.
*/
private PassFactory getCustomPasses(
final CustomPassExecutionTime executionTime) {
return new PassFactory("runCustomPasses", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return runInSerial(options.customPasses.get(executionTime));
}
};
}
/**
* All inlining is forbidden in heuristic renaming mode, because inlining
* will ruin the invariants that it depends on.
*/
private boolean isInliningForbidden() {
return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC ||
options.propertyRenaming ==
PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC;
}
/** Create a compiler pass that runs the given passes in serial. */
private static CompilerPass runInSerial(final CompilerPass ... passes) {
return runInSerial(Lists.newArrayList(passes));
}
/** Create a compiler pass that runs the given passes in serial. */
private static CompilerPass runInSerial(
final Collection<CompilerPass> passes) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
for (CompilerPass pass : passes) {
pass.process(externs, root);
}
}
};
}
@VisibleForTesting
static Map<String, Node> getAdditionalReplacements(
CompilerOptions options) {
Map<String, Node> additionalReplacements = Maps.newHashMap();
if (options.markAsCompiled || options.closurePass) {
additionalReplacements
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.put(COMPILED_CONSTANT_NAME, IR.trueNode());
}
if (options.closurePass && options.locale != null) {
additionalReplacements.put(CLOSURE_LOCALE_CONSTANT_NAME,
IR.string(options.locale));
}
return additionalReplacements;
}
final PassFactory printNameReferenceGraph =
new PassFactory("printNameReferenceGraph", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
NameReferenceGraphConstruction gc =
new NameReferenceGraphConstruction(compiler);
gc.process(externs, jsRoot);
String graphFileName = options.nameReferenceGraphPath;
try {
Files.write(DotFormatter.toDot(gc.getNameReferenceGraph()),
new File(graphFileName),
Charsets.UTF_8);
} catch (IOException e) {
compiler.report(
JSError.make(
NAME_REF_GRAPH_FILE_ERROR, e.getMessage(), graphFileName));
}
}
};
}
};
final PassFactory printNameReferenceReport =
new PassFactory("printNameReferenceReport", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
NameReferenceGraphConstruction gc =
new NameReferenceGraphConstruction(compiler);
String reportFileName = options.nameReferenceReportPath;
try {
NameReferenceGraphReport report =
new NameReferenceGraphReport(gc.getNameReferenceGraph());
Files.write(report.getHtmlReport(),
new File(reportFileName),
Charsets.UTF_8);
} catch (IOException e) {
compiler.report(
JSError.make(
NAME_REF_REPORT_FILE_ERROR,
e.getMessage(),
reportFileName));
}
}
};
}
};
/**
* A pass-factory that is good for {@code HotSwapCompilerPass} passes.
*/
abstract static class HotSwapPassFactory extends PassFactory {
HotSwapPassFactory(String name, boolean isOneTimePass) {
super(name, isOneTimePass);
}
@Override
protected abstract HotSwapCompilerPass create(AbstractCompiler compiler);
@Override
HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) {
return this.create(compiler);
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.jstype.StaticScope;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* CodingConvention defines a set of hooks to customize the behavior of the
* Compiler for a specific team/company.
*
*/
public interface CodingConvention extends Serializable {
/**
* This checks whether a given variable name, such as a name in all-caps
* should be treated as if it had the @const annotation.
*
* @param variableName potentially constant variable name
* @return {@code true} if the name should be treated as a constant.
*/
public boolean isConstant(String variableName);
/**
* This checks whether a given key of an object literal, such as a
* name in all-caps should be treated as if it had the @const
* annotation.
*/
public boolean isConstantKey(String keyName);
/**
* This checks that a given {@code key} may be used as a key for an enum.
*
* @param key the potential key to an enum
* @return {@code true} if the {@code key} may be used as an enum key,
* {@code false} otherwise
*/
public boolean isValidEnumKey(String key);
/**
* This checks whether a given parameter name should be treated as an
* optional parameter as far as type checking or function call arg count
* checking is concerned. Note that an optional function parameter may be
* declared as a simple type and is automatically converted to a union of the
* declared type and Undefined.
*
* @param parameter The parameter's node.
* @return {@code true} if the parameter should be treated as an optional
* parameter.
*/
public boolean isOptionalParameter(Node parameter);
/**
* This checks whether a given parameter should be treated as a marker
* for a variable argument list function. A VarArgs parameter must be the
* last parameter in a function declaration.
*
* @param parameter The parameter's node.
* @return {@code true} if the parameter should
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>param alternates the alternates of the union
*/
UnionType(JSTypeRegistry registry, Collection<JSType> alternates) {
super(registry);
this.alternates = alternates;
this.hashcode = this.alternates.hashCode();
}
/**
* Gets the alternate types of this union type.
* @return The alternate types of this union type. The returned set is
* immutable.
*/
public Collection<JSType> getAlternates() {
for (JSType t : alternates) {
if (t.isUnionType()) {
rebuildAlternates();
break;
}
}
return alternates;
}
/**
* Use UnionTypeBuilder to rebuild the list of alternates and hashcode
* of the current UnionType.
*/
private void rebuildAlternates() {
UnionTypeBuilder builder = new UnionTypeBuilder(registry);
for (JSType alternate : alternates) {
builder.addAlternate(alternate);
}
alternates = builder.getAlternates();
hashcode = alternates.hashCode();
}
/**
* This predicate is used to test whether a given type can appear in a
* numeric context, such as an operand of a multiply operator.
*
* @return true if the type can appear in a numeric context.
*/
@Override
public boolean matchesNumberContext() {
// TODO(user): Reverse this logic to make it correct instead of generous.
for (JSType t : alternates) {
if (t.matchesNumberContext()) {
return true;
}
}
return false;
}
/**
* This predicate is used to test whether a given type can appear in a
* {@code String} context, such as an operand of a string concat ({@code +})
* operator.<p>
*
* All types have at least the potential for converting to {@code String}.
* When we add externally defined types, such as a browser OM, we may choose
* to add types that do not automatically convert to {@code String}.
*
* @return {@code true} if not {@link VoidType}
*/
@Override
public boolean matchesStringContext() {
// TODO(user): Reverse this logic to make it correct instead of generous.
for (JSType t : alternates) {
if (t.matchesStringContext()) {
return true;
}
}
return false;
}
/**
* This predicate is used to test whether a given type can appear in an
* {@code Object} context, such as the expression in a {@code with}
* statement.<p>
*
* Most types we will encounter, except notably {@code null}, have at least
* the potential for converting to {@code Object}. Host defined objects can
* get peculiar.<p>
*
* VOID type is included here because while it is not part of the JavaScript
* language, functions returning 'void' type can't be used as operands of
* any operator or statement.<p>
*
* @return {@code true} if the type is not {@link NullType} or
* {@link VoidType}
*/
@Override
public boolean matchesObjectContext() {
// TODO(user): Reverse this logic to make it correct instead of generous.
for (
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>JSType t : alternates) {
if (t.matchesObjectContext()) {
return true;
}
}
return false;
}
@Override
public JSType findPropertyType(String propertyName) {
JSType propertyType = null;
for (JSType alternate : getAlternates()) {
// Filter out the null/undefined type.
if (alternate.isNullType() || alternate.isVoidType()) {
continue;
}
JSType altPropertyType = alternate.findPropertyType(propertyName);
if (altPropertyType == null) {
continue;
}
if (propertyType == null) {
propertyType = altPropertyType;
} else {
propertyType = propertyType.getLeastSupertype(altPropertyType);
}
}
return propertyType;
}
@Override
public boolean canBeCalled() {
for (JSType t : alternates) {
if (!t.canBeCalled()) {
return false;
}
}
return true;
}
@Override
public JSType autobox() {
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType t : alternates) {
restricted.addAlternate(t.autobox());
}
return restricted.build();
}
@Override
public JSType restrictByNotNullOrUndefined() {
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType t : alternates) {
restricted.addAlternate(t.restrictByNotNullOrUndefined());
}
return restricted.build();
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = null;
for (JSType t : alternates) {
TernaryValue test = t.testForEquality(that);
if (result == null) {
result = test;
} else if (!result.equals(test)) {
return UNKNOWN;
}
}
return result;
}
/**
* This predicate determines whether objects of this type can have the
* {@code null} value, and therefore can appear in contexts where
* {@code null} is expected.
*
* @return {@code true} for everything but {@code Number} and
* {@code Boolean} types.
*/
@Override
public boolean isNullable() {
for (JSType t : alternates) {
if (t.isNullable()) {
return true;
}
}
return false;
}
@Override
public boolean isUnknownType() {
for (JSType t : alternates) {
if (t.isUnknownType()) {
return true;
}
}
return false;
}
@Override
public boolean isStruct() {
for (JSType typ : getAlternates()) {
if (typ.isStruct()) {
return true;
}
}
return false;
}
@Override
public boolean isDict() {
for (JSType typ : getAlternates()) {
if (typ.isDict()) {
return true;
}
}
return false;
}
@Override
public JSType getLeastSupertype(JSType that) {
if (!that.isUnknownType() && !that.isUnionType()) {
for (JSType alternate : alternates) {
if (!alternate.isUnknownType() && that
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.isSubtype(alternate)) {
return this;
}
}
}
return getLeastSupertype(this, that);
}
JSType meet(JSType that) {
UnionTypeBuilder builder = new UnionTypeBuilder(registry);
for (JSType alternate : alternates) {
if (alternate.isSubtype(that)) {
builder.addAlternate(alternate);
}
}
if (that.isUnionType()) {
for (JSType otherAlternate : that.toMaybeUnionType().alternates) {
if (otherAlternate.isSubtype(this)) {
builder.addAlternate(otherAlternate);
}
}
} else if (that.isSubtype(this)) {
builder.addAlternate(that);
}
JSType result = builder.build();
if (!result.isNoType()) {
return result;
} else if (this.isObject() && (that.isObject() && !that.isNoType())) {
return getNativeType(JSTypeNative.NO_OBJECT_TYPE);
} else {
return getNativeType(JSTypeNative.NO_TYPE);
}
}
/**
* Two union types are equal if, after flattening nested union types,
* they have the same number of alternates and all alternates are equal.
*/
boolean checkUnionEquivalenceHelper(
UnionType that, EquivalenceMethod eqMethod) {
Collection<JSType> thatAlternates = that.getAlternates();
if (eqMethod == EquivalenceMethod.IDENTITY
&& getAlternates().size() != thatAlternates.size()) {
return false;
}
for (JSType alternate : thatAlternates) {
if (!hasAlternate(alternate, eqMethod)) {
return false;
}
}
return true;
}
private boolean hasAlternate(JSType type, EquivalenceMethod eqMethod) {
for (JSType alternate : getAlternates()) {
if (alternate.checkEquivalenceHelper(type, eqMethod)) {
return true;
}
}
return false;
}
@Override
public boolean hasProperty(String pname) {
for (JSType alternate : alternates) {
if (alternate.hasProperty(pname)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return this.hashcode;
}
@Override
public UnionType toMaybeUnionType() {
return this;
}
@Override
public boolean isObject() {
for (JSType alternate : alternates) {
if (!alternate.isObject()) {
return false;
}
}
return true;
}
/**
* A {@link UnionType} contains a given type (alternate) iff the member
* vector contains it.
*
* @param type The alternate which might be in this union.
*
* @return {@code true} if the alternate is in the union
*/
public boolean contains(JSType type) {
for (JSType alt : alternates) {
if (alt.isEquivalentTo(type)) {
return true;
}
}
return false;
}
/**
* Returns a more restricted union type than {@code this} one, in which all
* subtypes of {@code type} have been removed.<p>
*
*
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> Examples:
* <ul>
* <li>{@code (number,string)} restricted by {@code number} is
* {@code string}</li>
* <li>{@code (null, EvalError, URIError)} restricted by
* {@code Error} is {@code null}</li>
* </ul>
*
* @param type the supertype of the types to remove from this union type
*/
public JSType getRestrictedUnion(JSType type) {
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType t : alternates) {
// Keep all unknown/unresolved types.
if (t.isUnknownType() || t.isNoResolvedType() || !t.isSubtype(type)) {
restricted.addAlternate(t);
}
}
return restricted.build();
}
@Override String toStringHelper(boolean forAnnotations) {
StringBuilder result = new StringBuilder();
boolean firstAlternate = true;
result.append("(");
SortedSet<JSType> sorted = new TreeSet<JSType>(ALPHA);
sorted.addAll(alternates);
for (JSType t : sorted) {
if (!firstAlternate) {
result.append("|");
}
result.append(t.toStringHelper(forAnnotations));
firstAlternate = false;
}
result.append(")");
return result.toString();
}
@Override
public boolean isSubtype(JSType that) {
// unknown
if (that.isUnknownType()) {
return true;
}
// all type
if (that.isAllType()) {
return true;
}
for (JSType element : alternates) {
if (!element.isSubtype(that)) {
return false;
}
}
return true;
}
@Override
public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) {
// gather elements after restriction
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType element : alternates) {
restricted.addAlternate(
element.getRestrictedTypeGivenToBooleanOutcome(outcome));
}
return restricted.build();
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
BooleanLiteralSet literals = BooleanLiteralSet.EMPTY;
for (JSType element : alternates) {
literals = literals.union(element.getPossibleToBooleanOutcomes());
if (literals == BooleanLiteralSet.BOTH) {
break;
}
}
return literals;
}
@Override
public TypePair getTypesUnderEquality(JSType that) {
UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry);
UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry);
for (JSType element : alternates) {
TypePair p = element.getTypesUnderEquality(that);
if (p.typeA != null) {
thisRestricted.addAlternate(p.typeA);
}
if (p.typeB != null) {
thatRestricted.addAlternate(p.typeB);
}
}
return new TypePair(
thisRestricted.build(),
thatRestricted.build());
}
@Override
public TypePair getTypesUnderInequality(JSType that) {
UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry);
UnionType
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Builder thatRestricted = new UnionTypeBuilder(registry);
for (JSType element : alternates) {
TypePair p = element.getTypesUnderInequality(that);
if (p.typeA != null) {
thisRestricted.addAlternate(p.typeA);
}
if (p.typeB != null) {
thatRestricted.addAlternate(p.typeB);
}
}
return new TypePair(
thisRestricted.build(),
thatRestricted.build());
}
@Override
public TypePair getTypesUnderShallowInequality(JSType that) {
UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry);
UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry);
for (JSType element : alternates) {
TypePair p = element.getTypesUnderShallowInequality(that);
if (p.typeA != null) {
thisRestricted.addAlternate(p.typeA);
}
if (p.typeB != null) {
thatRestricted.addAlternate(p.typeB);
}
}
return new TypePair(
thisRestricted.build(),
thatRestricted.build());
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseUnionType(this);
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseUnionType(this, that);
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setResolvedTypeInternal(this); // for circularly defined types.
// Just resolve the alternates, but do not update as that breaks some error
// reporting cases.
for (JSType alternate : alternates) {
alternate.resolve(t, scope);
}
// Ensure the union is in a normalized state.
rebuildAlternates();
return this;
}
@Override
public String toDebugHashCodeString() {
List<String> hashCodes = Lists.newArrayList();
for (JSType a : alternates) {
hashCodes.add(a.toDebugHashCodeString());
}
return "{(" + Joiner.on(",").join(hashCodes) + ")}";
}
@Override
public boolean setValidator(Predicate<JSType> validator) {
for (JSType a : alternates) {
a.setValidator(validator);
}
return true;
}
@Override
public JSType collapseUnion() {
JSType currentValue = null;
ObjectType currentCommonSuper = null;
for (JSType a : alternates) {
if (a.isUnknownType()) {
return getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
ObjectType obj = a.toObjectType();
if (obj == null) {
if (currentValue == null && currentCommonSuper == null) {
// If obj is not an object, then it must be a value.
currentValue = a;
} else {
// Multiple values and objects will always collapse to the ALL_TYPE.
return getNativeType(JSTypeNative.ALL_TYPE);
}
} else if (currentValue != null) {
// Values and objects will always collapse to the ALL_TYPE.
return getNativeType(JSTypeNative
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.ALL_TYPE);
} else if (currentCommonSuper == null) {
currentCommonSuper = obj;
} else {
currentCommonSuper =
registry.findCommonSuperObject(currentCommonSuper, obj);
}
}
return currentCommonSuper;
}
@Override
public void matchConstraint(JSType constraint) {
for (JSType alternate : alternates) {
alternate.matchConstraint(constraint);
}
}
@Override
public boolean hasAnyTemplateTypesInternal() {
for (JSType alternate : alternates) {
if (alternate.hasAnyTemplateTypes()) {
return true;
}
}
return false;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
/**
* The bottom Object type, representing the subclass of all objects.
*
* Although JavaScript programmers can't explicitly denote the bottom
* Object type, it comes up in static analysis. For example, if we have:
* <code>
* var x = function() {};
* if (x instanceof Array) {
* f(x);
* }
* </code>
* We need to be able to assign {@code x} a type within the {@code f(x)}
* call. It has no possible type, but {@code x} would not be legal if f
* expected a string. So we assign it the {@code NoObjectType}.
*
* @see <a href="http://en.wikipedia.org/wiki/Bottom_type">Bottom types</a>
*/
public class NoObjectType extends FunctionType {
private static final long serialVersionUID = 1L;
NoObjectType(JSTypeRegistry registry) {
super(registry, null, null,
registry.createArrowType(null, null),
null, null, true, true);
getInternalArrowType().returnType = this;
this.setInstanceType(this);
}
@Override
public boolean isSubtype(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>JSType that) {
if (JSType.isSubtypeHelper(this, that)) {
return true;
} else {
return that.isObject() && !that.isNoType() && !that.isNoResolvedType();
}
}
@Override
public FunctionType toMaybeFunctionType() {
return null;
}
@Override
public boolean isNoObjectType() {
return true;
}
@Override
public ObjectType getImplicitPrototype() {
return null;
}
@Override
public String getReferenceName() {
return null;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, Node propertyNode) {
// nothing, all properties are defined
return true;
}
@Override
public boolean removeProperty(String name) {
return false;
}
@Override
public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) {
// Do nothing, specific properties do not have JSDocInfo.
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNoObjectType();
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseNoObjectType(that);
}
@Override
String toStringHelper(boolean forAnnotations) {
return forAnnotations ? "?" : "NoObject";
}
@Override
public FunctionType getConstructor() {
return null;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
return this;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> Find marker's description (if applicable).
if (jsdocBuilder.shouldParseDocumentation()) {
ExtractionInfo descriptionInfo = extractMultilineTextualBlock(token);
token = descriptionInfo.token;
} else {
token = eatTokensUntilEOL(token);
}
return token;
}
private void checkExtendedTypes(List<ExtendedTypeInfo> extendedTypes) {
for (ExtendedTypeInfo typeInfo : extendedTypes) {
// If interface, record the multiple extended interfaces
if (jsdocBuilder.isInterfaceRecorded()) {
if (!jsdocBuilder.recordExtendedInterface(typeInfo.type)) {
parser.addParserWarning("msg.jsdoc.extends.duplicate",
typeInfo.lineno, typeInfo.charno);
}
} else {
if (!jsdocBuilder.recordBaseType(typeInfo.type)) {
parser.addTypeWarning("msg.jsdoc.incompat.type",
typeInfo.lineno, typeInfo.charno);
}
}
}
}
/**
* Parse a {@code @suppress} tag of the form
* {@code @suppress{warning1|warning2}}.
*
* @param token The current token.
*/
private JsDocToken parseSuppressTag(JsDocToken token) {
if (token == JsDocToken.LC) {
Set<String> suppressions = new HashSet<String>();
while (true) {
if (match(JsDocToken.STRING)) {
String name = stream.getString();
if (!suppressionNames.contains(name)) {
parser.addParserWarning("msg.jsdoc.suppress.unknown", name,
stream.getLineno(), stream.getCharno());
}
suppressions.add(stream.getString());
token = next();
} else {
parser.addParserWarning("msg.jsdoc.suppress",
stream.getLineno(), stream.getCharno());
return token;
}
if (match(JsDocToken.PIPE, JsDocToken.COMMA)) {
token = next();
} else {
break;
}
}
if (!match(JsDocToken.RC)) {
parser.addParserWarning("msg.jsdoc.suppress",
stream.getLineno(), stream.getCharno());
} else {
token = next();
if (!jsdocBuilder.recordSuppressions(suppressions)) {
parser.addParserWarning("msg.jsdoc.suppress.duplicate",
stream.getLineno(), stream.getCharno());
}
}
}
return token;
}
/**
* Parse a {@code @modifies} tag of the form
* {@code @modifies{this|arguments|param}}.
*
* @param token The current token.
*/
private JsDocToken parseModifiesTag(JsDocToken token) {
if (token == JsDocToken.LC) {
Set<String> modifies = new HashSet<String>();
while (true) {
if (match(JsDocToken.STRING)) {
String name = stream.getString();
if (!modifiesAnnotationKeywords.contains(name)
&& !jsdocBuilder.hasParameter(name)) {
parser.addParserWarning("msg.jsdoc.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>modifies.unknown", name,
stream.getLineno(), stream.getCharno());
}
modifies.add(stream.getString());
token = next();
} else {
parser.addParserWarning("msg.jsdoc.modifies",
stream.getLineno(), stream.getCharno());
return token;
}
if (match(JsDocToken.PIPE)) {
token = next();
} else {
break;
}
}
if (!match(JsDocToken.RC)) {
parser.addParserWarning("msg.jsdoc.modifies",
stream.getLineno(), stream.getCharno());
} else {
token = next();
if (!jsdocBuilder.recordModifies(modifies)) {
parser.addParserWarning("msg.jsdoc.modifies.duplicate",
stream.getLineno(), stream.getCharno());
}
}
}
return token;
}
/**
* Parse a {@code @idgenerator} tag of the form
* {@code @idgenerator} or
* {@code @idgenerator{consistent}}.
*
* @param token The current token.
*/
private JsDocToken parseIdGeneratorTag(JsDocToken token) {
String idgenKind = "unique";
if (token == JsDocToken.LC) {
if (match(JsDocToken.STRING)) {
String name = stream.getString();
if (!idGeneratorAnnotationKeywords.contains(name)
&& !jsdocBuilder.hasParameter(name)) {
parser.addParserWarning("msg.jsdoc.idgen.unknown", name,
stream.getLineno(), stream.getCharno());
}
idgenKind = name;
token = next();
} else {
parser.addParserWarning("msg.jsdoc.idgen.bad",
stream.getLineno(), stream.getCharno());
return token;
}
if (!match(JsDocToken.RC)) {
parser.addParserWarning("msg.jsdoc.idgen.bad",
stream.getLineno(), stream.getCharno());
} else {
token = next();
}
}
if (idgenKind.equals("unique")) {
if (!jsdocBuilder.recordIdGenerator()) {
parser.addParserWarning("msg.jsdoc.idgen.duplicate",
stream.getLineno(), stream.getCharno());
}
} else if (idgenKind.equals("consistent")) {
if (!jsdocBuilder.recordConsistentIdGenerator()) {
parser.addParserWarning("msg.jsdoc.idgen.duplicate",
stream.getLineno(), stream.getCharno());
}
} else if (idgenKind.equals("stable")) {
if (!jsdocBuilder.recordStableIdGenerator()) {
parser.addParserWarning("msg.jsdoc.idgen.duplicate",
stream.getLineno(), stream.getCharno());
}
} else if (idgenKind.equals("mapped")) {
if (!jsdocBuilder.recordMappedIdGenerator()) {
parser.addParserWarning("msg.jsdoc.idgen.duplicate",
stream.getLineno(), stream.getCharno());
}
}
return token
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* John Lenz
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
/**
* A "can cast to" relationship visitor.
*/
class CanCastToVisitor implements RelationshipVisitor<Boolean> {
@Override
public Boolean caseUnknownType(JSType thisType, JSType thatType) {
return true;
}
@Override
public Boolean caseNoType(JSType thatType) {
return true;
}
@Override
public Boolean caseNoObjectType(JSType thatType) {
return true; // TODO(johnlenz): restrict to objects
}
@Override
public Boolean caseAllType(JSType thatType) {
return true;
}
boolean canCastToUnion(JSType thisType, UnionType unionType) {
for (JSType type : unionType.getAlternates()) {
if (thisType.visit(this, type)) {
return true;
}
}
return false;
}
boolean canCastToFunction(JSType thisType, FunctionType functionType) {
if (thisType.isFunctionType()) {
// TODO(johnlenz): visit function parts
return true;
} else {
return thisType.isSubtype(functionType)
|| functionType.isSubtype(thisType);
}
}
private boolean isInterface(JSType type) {
ObjectType objType = type.toObjectType();
if (objType != null) {
JSType constructor = objType.getConstructor();
return constructor != null
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> && constructor.isInterface();
}
return false;
}
Boolean castCastToHelper(JSType thisType, JSType thatType) {
if (thatType.isUnknownType()
|| thatType.isAllType()
|| thatType.isNoObjectType() // TODO(johnlenz): restrict to objects
|| thatType.isNoType()) {
return true;
} else if (thisType.isRecordType() || thatType.isRecordType()) {
return true; // TODO(johnlenz): are there any misuses we can catch?
} else if (isInterface(thisType) || isInterface(thatType)) {
return true; // TODO(johnlenz): are there any misuses we can catch?
} else if (thatType.isEnumElementType()) {
return thisType.visit(this,
thatType.toMaybeEnumElementType().getPrimitiveType());
} else if (thatType.isUnionType()) {
return canCastToUnion(thisType, thatType.toMaybeUnionType());
} else if (thatType.isFunctionType()) {
return canCastToFunction(thisType, thatType.toMaybeFunctionType());
} else if (thatType.isTemplatizedType()) {
// TODO(johnlenz): once the templated type work is finished,
// restrict the type parameters.
return thisType.visit(this,
thatType.toMaybeTemplatizedType().getReferencedTypeInternal());
}
return thisType.isSubtype(thatType) || thatType.isSubtype(thisType);
}
@Override
public Boolean caseValueType(ValueType thisType, JSType thatType) {
return castCastToHelper(thisType, thatType);
}
@Override
public Boolean caseObjectType(ObjectType thisType, JSType thatType) {
return castCastToHelper(thisType, thatType);
}
@Override
public Boolean caseFunctionType(FunctionType thisType, JSType thatType) {
return castCastToHelper(thisType, thatType);
}
@Override
public Boolean caseUnionType(UnionType thisType, JSType thatType) {
boolean visited = false;
for (JSType type : thisType.getAlternates()) {
if (type.isVoidType() || type.isNullType()) {
// Don't allow if the only match between the types is null or void,
// otherwise any nullable type would be castable to any other nullable
// type and we don't want that.
} else {
visited = true;
if (type.visit(this, thatType)) {
return true;
}
}
}
// Special case the "null|undefined" union and allow it to be cast
// to any cast to any type containing allowing either null|undefined.
if (!visited) {
JSType NULL_TYPE = thisType.getNativeType(JSTypeNative.NULL_TYPE);
JSType VOID_TYPE = thisType.getNativeType(JSTypeNative.VOID_TYPE);
return NULL_TYPE.visit(this, thatType) || VOID_TYPE.visit(this, thatType);
}
return false;
}
@Override
public Boolean caseTemplatizedType(
TemplatizedType thisType, JSType thatType) {
// TODO(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>johnlenz): once the templated type work is finished,
// restrict the type parameters.
return thisType.getReferencedTypeInternal().visit(this, thatType);
}
@Override
public Boolean caseTemplateType(TemplateType thisType, JSType thatType) {
return true;
}
@Override
public Boolean caseEnumElementType(
EnumElementType typeType, JSType thatType) {
return typeType.getPrimitiveType().visit(this, thatType);
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> 1L;
public GraphAnnotationState(int size) {
super(size);
}
}
/**
* Used by {@link #pushNodeAnnotations()} and {@link #popNodeAnnotations()}.
*/
private Deque<GraphAnnotationState> nodeAnnotationStack;
/**
* Used by {@link #pushEdgeAnnotations()} and {@link #popEdgeAnnotations()}.
*/
private Deque<GraphAnnotationState> edgeAnnotationStack;
/**
* Connects two nodes in the graph with an edge.
*
* @param n1 First node.
* @param edge The edge.
* @param n2 Second node.
*/
public abstract void connect(N n1, E edge, N n2);
/**
* Disconnects two nodes in the graph by removing all edges between them.
*
* @param n1 First node.
* @param n2 Second node.
*/
public abstract void disconnect(N n1, N n2);
/**
* Connects two nodes in the graph with an edge if such edge does not already
* exists between the nodes.
*
* @param n1 First node.
* @param edge The edge.
* @param n2 Second node.
*/
public final void connectIfNotFound(N n1, E edge, N n2) {
if (!isConnected(n1, edge, n2)) {
connect(n1, edge, n2);
}
}
/**
* Gets a node from the graph given a value. New nodes are created if that
* value has not been assigned a graph node. Values equality are compared
* using <code>Object.equals</code>.
*
* @param value The node's value.
* @return The corresponding node in the graph.
*/
public abstract GraphNode<N, E> createNode(N value);
/** Gets an immutable list of all nodes. */
@Override
public abstract Collection<? extends GraphNode<N, E>> getNodes();
/** Gets an immutable list of all edges. */
public abstract List<? extends GraphEdge<N, E>> getEdges();
/**
* Gets the degree of a node.
*
* @param value The node's value.
* @return The degree of the node.
*/
public abstract int getNodeDegree(N value);
@Override
public int getWeight(N value) {
return getNodeDegree(value);
}
/**
* Gets the neighboring nodes.
*
* @param value The node's value.
* @return A list of neighboring nodes.
*/
public abstract List<GraphNode<N, E>> getNeighborNodes(N value);
public abstract Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value);
/**
* Retrieves an edge from the graph.
*
* @param n1 Node one.
* @param n2 Node two.
* @return The list of edges between those two values in the graph.
*/
public abstract List<? extends GraphEdge<N, E>> getEdges(N n1, N n2);
/**
* Retrieves any edge from the graph.
*
* @param n1 Node one.
* @param n2 Node two.
* @return The first edges between those two values in the graph. null if
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> * there are none.
*/
public abstract GraphEdge<N, E> getFirstEdge(N n1, N n2);
/**
* Checks whether the node exists in the graph ({@link #createNode(Object)}
* has been called with that value).
*
* @param n Node.
* @return <code>true</code> if it exist.
*/
public final boolean hasNode(N n) {
return getNode(n) != null;
}
/**
* Checks whether two nodes in the graph are connected.
*
* @param n1 Node 1.
* @param n2 Node 2.
* @return <code>true</code> if the two nodes are connected.
*/
public abstract boolean isConnected(N n1, N n2);
/**
* Checks whether two nodes in the graph are connected by the given
* edge type.
*
* @param n1 Node 1.
* @param e The edge type.
* @param n2 Node 2.
*/
public abstract boolean isConnected(N n1, E e, N n2);
/**
* Gets the node of the specified type, or throws an
* IllegalArgumentException.
*/
@SuppressWarnings("unchecked")
<T extends GraphNode<N, E>> T getNodeOrFail(N val) {
T node = (T) getNode(val);
if (node == null) {
throw new IllegalArgumentException(val + " does not exist in graph");
}
return node;
}
@Override
public final void clearNodeAnnotations() {
for (GraphNode<N, E> n : getNodes()) {
n.setAnnotation(null);
}
}
/** Makes each edge's annotation null. */
public final void clearEdgeAnnotations() {
for (GraphEdge<N, E> e : getEdges()) {
e.setAnnotation(null);
}
}
/**
* Pushes nodes' annotation values. Restored with
* {@link #popNodeAnnotations()}. Nodes' annotation values are cleared.
*/
public final void pushNodeAnnotations() {
if (nodeAnnotationStack == null) {
nodeAnnotationStack = Lists.newLinkedList();
}
pushAnnotations(nodeAnnotationStack, getNodes());
}
/**
* Restores nodes' annotation values to state before last
* {@link #pushNodeAnnotations()}.
*/
public final void popNodeAnnotations() {
Preconditions.checkNotNull(nodeAnnotationStack,
"Popping node annotations without pushing.");
popAnnotations(nodeAnnotationStack);
}
/**
* Pushes edges' annotation values. Restored with
* {@link #popEdgeAnnotations()}. Edges' annotation values are cleared.
*/
public final void pushEdgeAnnotations() {
if (edgeAnnotationStack == null) {
edgeAnnotationStack = Lists.newLinkedList();
}
pushAnnotations(edgeAnnotationStack, getEdges());
}
/**
* Restores edges' annotation values to state before last
* {@link #pushEdgeAnnotations()}.
*/
public final void popEdgeAnnotations() {
Preconditions.checkNotNull(edgeAnnotationStack,
"Popping edge annotations without pushing.");
popAnnotations(edgeAnnotationStack);
}
/**
* A generic edge.
*
* @param <N> Value type that the graph node stores.
*
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> @param <E> Value type that the graph edge stores.
*/
public interface GraphEdge<N, E> extends Annotatable {
/**
* Retrieves the edge's value.
*
* @return The value.
*/
E getValue();
GraphNode<N, E> getNodeA();
GraphNode<N, E> getNodeB();
}
/**
* A simple implementation of SubGraph that calculates adjacency by iterating
* over a node's neighbors.
*/
class SimpleSubGraph<N, E> implements SubGraph<N, E> {
private Graph<N, E> graph;
private List<GraphNode<N, E>> nodes = Lists.newArrayList();
SimpleSubGraph(Graph<N, E> graph) {
this.graph = graph;
}
@Override
public boolean isIndependentOf(N value) {
GraphNode<N, E> node = graph.getNode(value);
for (GraphNode<N, E> n : nodes) {
if (graph.getNeighborNodes(n.getValue()).contains(node)) {
return false;
}
}
return true;
}
@Override
public void addNode(N value) {
nodes.add(graph.getNodeOrFail(value));
}
}
/**
* Pushes a new list on stack and stores nodes annotations in the new list.
* Clears objects' annotations as well.
*/
private static void pushAnnotations(
Deque<GraphAnnotationState> stack,
Collection<? extends Annotatable> haveAnnotations) {
stack.push(new GraphAnnotationState(haveAnnotations.size()));
for (Annotatable h : haveAnnotations) {
stack.peek().add(new AnnotationState(h, h.getAnnotation()));
h.setAnnotation(null);
}
}
/**
* Restores the node annotations on the top of stack and pops stack.
*/
private static void popAnnotations(Deque<GraphAnnotationState> stack) {
for (AnnotationState as : stack.pop()) {
as.first.setAnnotation(as.second);
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Predicate;
import com.google.javascript.jscomp.ControlFlowGraph.Branch;
import com.google.javascript.jscomp.NodeTraversal.ScopedCallback;
import com.google.javascript.jscomp.graph.GraphNode;
import com.google.javascript.jscomp.graph.GraphReachability;
import com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.TernaryValue;
/**
* Use {@link ControlFlowGraph} and {@link GraphReachability} to inform user
* about unreachable code.
*
*/
class CheckUnreachableCode implements ScopedCallback {
static final DiagnosticType UNREACHABLE_CODE = DiagnosticType.error(
"JSC_UNREACHABLE_CODE", "unreachable code");
private final AbstractCompiler compiler;
private final CheckLevel level;
CheckUnreachableCode(AbstractCompiler compiler, CheckLevel level) {
this.compiler = compiler;
this.level = level;
}
@Override
public void enterScope(NodeTraversal t) {
initScope(t.getControlFlowGraph());
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
GraphNode<Node, Branch> gNode = t.getControlFlowGraph().getNode(n);
if (gNode != null && gNode.getAnnotation() != GraphReachability.REACHABLE) {
// Only report error when there are some line number informations.
// There are synthetic nodes with no line number informations, nodes
// introduce by other passes (although not likely since this pass should
// be executed early) or some rhino bug.
if (n.getLineno() != -1 &&
// Allow spurious semi-colons and spurious breaks.
!n.isEmpty() && !n.isBreak()) {
compiler.report(t.makeError(n, level, UNREACHABLE_CODE));
// From now on, we are going to assume the user fixed the error and not
// give more warning related to code section reachable from this node.
new GraphReachability<Node, ControlFlowGraph.Branch>(
t.getControlFlowGraph()).recompute(n);
// Saves time by not traversing children.
return false;
}
}
return true;
}
private void initScope(ControlFlowGraph<Node> controlFlowGraph) {
new GraphReachability<Node,
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> ControlFlowGraph.Branch>(
controlFlowGraph, new ReachablePredicate()).compute(
controlFlowGraph.getEntry().getValue());
}
@Override
public void exitScope(NodeTraversal t) {
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
}
private final class ReachablePredicate implements
Predicate<EdgeTuple<Node, ControlFlowGraph.Branch>> {
@Override
public boolean apply(EdgeTuple<Node, Branch> input) {
Branch branch = input.edge;
if (!branch.isConditional()) {
return true;
}
Node predecessor = input.sourceNode;
Node condition = NodeUtil.getConditionExpression(predecessor);
// TODO(user): Handle more complicated expression like true == true,
// etc....
if (condition != null) {
TernaryValue val = NodeUtil.getImpureBooleanValue(condition);
if (val != TernaryValue.UNKNOWN) {
return val.toBoolean(true) == (branch == Branch.ON_TRUE);
}
}
return true;
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
/**
* Abstract message formatter providing default behavior for implementations
* of {@link MessageFormatter} needing a {@link SourceExcerptProvider}.
*
*/
public abstract class AbstractMessageFormatter implements MessageFormatter {
private final SourceExcerptProvider source;
private boolean colorize;
public AbstractMessageFormatter(SourceExcerptProvider source) {
this.source = source;
}
public void setColorize(boolean colorize) {
this.colorize = colorize;
}
/**
* Get the source excerpt provider.
*/
protected final SourceExcerptProvider getSource() {
return source;
}
private static final Set<String> SUPPORTED_COLOR_TERMINALS =
ImmutableSet.of("xterm",
"xterm-color",
"xterm-256color",
"screen-bce");
static boolean termSupportsColor(String term) {
return SUPPORTED_COLOR_TERMINALS.contains(term);
}
private static enum Color {
ERROR("\033[31m"),
WARNING("\033[35m"),
RESET("\033[39m");
private final String controlCharacter;
Color(String controlCharacter) {
this.controlCharacter = controlCharacter;
}
public String getControlCharacter() {
return controlCharacter;
}
}
String getLevelName(CheckLevel level) {
switch (level) {
case ERROR: return maybeColorize("ERROR", Color.ERROR);
case WARNING: return maybeColorize("WARNING", Color.WARNING);
default: return level.toString();
}
}
private String maybeColorize(String text, Color color) {
if (!colorize) {
return text;
}
return color.getControlCharacter() +
text + Color.RESET.getControlCharacter();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2012 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* Checks for common errors, such as misplaced semicolons:
* <pre>
* if (x); act_now();
* </pre>
* or comparison against NaN:
* <pre>
* if (x === NaN) act();
* </pre>
* and generates warnings.
*
* @author johnlenz@google.com (John Lenz)
*/
final class CheckSuspiciousCode extends AbstractPostOrderCallback {
static final DiagnosticType SUSPICIOUS_SEMICOLON = DiagnosticType.warning(
"JSC_SUSPICIOUS_SEMICOLON",
"If this if/for/while really shouldn't have a body, use {}");
static final DiagnosticType SUSPICIOUS_COMPARISON_WITH_NAN =
DiagnosticType.warning(
"JSC_SUSPICIOUS_NAN",
"Comparison again NaN is always false. Did you mean isNaN()?");
CheckSuspiciousCode() {
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
checkMissingSemicolon(t, n);
checkNaN(t, n);
}
private void checkMissingSemicolon(NodeTraversal t, Node n) {
switch (n.getType()) {
case Token.IF:
Node trueCase = n.getFirstChild().getNext();
reportIfWasEmpty(t, trueCase);
Node elseCase = trueCase.getNext();
if (elseCase != null) {
reportIfWasEmpty(t, elseCase);
}
break;
case Token.WHILE:
case Token.FOR:
reportIfWasEmpty(t, NodeUtil.getLoopCodeBlock(n));
break;
}
}
private void reportIfWasEmpty(NodeTraversal t, Node block) {
Preconditions.checkState(block.isBlock());
// A semicolon is distinguished from a block without children by
// annotating it with EMPTY_BLOCK. Blocks without children are
// usually intentional, especially with loops.
if (!block.hasChildren() && block.wasEmptyNode()) {
t.getCompiler().report(
t.makeError(block, SUSPICIOUS_SEMICOLON));
}
}
private void checkNaN(NodeTraversal t, Node n) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Ast(file), isExtern);
}
/** Returns a name for this input. Must be unique across all inputs. */
@Override
public InputId getInputId() {
return id;
}
/** Returns a name for this input. Must be unique across all inputs. */
@Override
public String getName() {
return id.getIdName();
}
public SourceAst getAst() {
return ast;
}
/** Gets the path relative to closure-base, if one is available. */
@Override
public String getPathRelativeToClosureBase() {
// TODO(nicksantos): Implement me.
throw new UnsupportedOperationException();
}
@Override
public Node getAstRoot(AbstractCompiler compiler) {
Node root = ast.getAstRoot(compiler);
// The root maybe null if the AST can not be created.
if (root != null) {
Preconditions.checkState(root.isScript());
Preconditions.checkNotNull(root.getInputId());
}
return root;
}
@Override
public void clearAst() {
ast.clearAst();
}
@Override
public SourceFile getSourceFile() {
return ast.getSourceFile();
}
@Override
public void setSourceFile(SourceFile file) {
ast.setSourceFile(file);
}
/** Returns the SourceAst object on which this input is based. */
public SourceAst getSourceAst() {
return ast;
}
/** Sets an abstract compiler for doing parsing. */
public void setCompiler(AbstractCompiler compiler) {
this.compiler = compiler;
}
private void checkErrorManager() {
Preconditions.checkNotNull(compiler,
"Expected setCompiler to be called first: " + this);
Preconditions.checkNotNull(compiler.getErrorManager(),
"Expected compiler to call an error manager: " + this);
}
/** Gets a list of types depended on by this input. */
@Override
public Collection<String> getRequires() {
checkErrorManager();
try {
regenerateDependencyInfoIfNecessary();
return Collections.<String>unmodifiableSet(requires);
} catch (IOException e) {
compiler.getErrorManager().report(CheckLevel.ERROR,
JSError.make(AbstractCompiler.READ_ERROR, getName()));
return ImmutableList.<String>of();
}
}
/** Gets a list of types provided by this input. */
@Override
public Collection<String> getProvides() {
checkErrorManager();
try {
regenerateDependencyInfoIfNecessary();
return Collections.<String>unmodifiableSet(provides);
} catch (IOException e) {
compiler.getErrorManager().report(CheckLevel.ERROR,
JSError.make(AbstractCompiler.READ_ERROR, getName()));
return ImmutableList.<String>of();
}
}
// TODO(nicksantos): Remove addProvide/addRequire/removeRequire once
// there is better support for discovering non-closure dependencies.
void addProvide(String provide) {
getProvides();
provides.add(provide);
}
void addRequire(String require) {
getRequires();
requires.add(require);
}
public void removeRequire(String require) {
getRequires();
requires.remove(require);
}
/**
* Regenerates the provides/requires if we need to do so.
*/
private void re
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> getSourceFile().getLine(lineNumber);
}
/**
* Get a region around the indicated line number. The exact definition of a
* region is implementation specific, but it must contain the line indicated
* by the line number. A region must not start or end by a carriage return.
*
* @param lineNumber the line number, 1 being the first line of the file.
* @return The line indicated. Returns {@code null} if it does not exist,
* or if there was an IO exception.
*/
public Region getRegion(int lineNumber) {
return getSourceFile().getRegion(lineNumber);
}
public String getCode() throws IOException {
return getSourceFile().getCode();
}
/** Returns the module to which the input belongs. */
public JSModule getModule() {
return module;
}
/** Sets the module to which the input belongs. */
public void setModule(JSModule module) {
// An input may only belong to one module.
Preconditions.checkArgument(
module == null || this.module == null || this.module == module);
this.module = module;
}
/** Overrides the module to which the input belongs. */
void overrideModule(JSModule module) {
this.module = module;
}
public boolean isExtern() {
if (ast == null || ast.getSourceFile() == null) {
return false;
}
return ast.getSourceFile().isExtern();
}
void setIsExtern(boolean isExtern) {
if (ast == null || ast.getSourceFile() == null) {
return;
}
ast.getSourceFile().setIsExtern(isExtern);
}
public int getLineOffset(int lineno) {
return ast.getSourceFile().getLineOffset(lineno);
}
/** @return The number of lines in this input. */
public int getNumLines() {
return ast.getSourceFile().getNumLines();
}
@Override
public String toString() {
return getName();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Warning(this);
default:
return null;
}
}
@Override
public String toString() {
// TODO(user): remove custom toString.
return type.key + ". " + description + " at " +
(sourceName != null && sourceName.length() > 0 ?
sourceName : "(unknown source)") + " line " +
(lineNumber != -1 ? String.valueOf(lineNumber) : "(unknown line)") +
" : " + (charno != -1 ? String.valueOf(charno) : "(unknown column)");
}
/**
* Get the character number.
*/
public int getCharno() {
return charno;
}
/**
* Get the line number. One-based.
*/
public int getLineNumber() {
return lineNumber;
}
/**
* @return the offset of the region the Error applies to, or -1 if the offset
* is unknown.
*/
public int getNodeSourceOffset() {
return node != null ? node.getSourceOffset() : -1;
}
/**
* @return the length of the region the Error applies to, or 0 if the length
* is unknown.
*/
public int getNodeLength() {
return node != null ? node.getLength() : 0;
}
/** The default level, before any of the WarningsGuards are applied. */
public CheckLevel getDefaultLevel() {
return defaultLevel;
}
@Override
public boolean equals(Object o) {
// Generated by Intellij IDEA
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
JSError jsError = (JSError) o;
if (charno != jsError.charno) {
return false;
}
if (lineNumber != jsError.lineNumber) {
return false;
}
if (!description.equals(jsError.description)) {
return false;
}
if (defaultLevel != jsError.defaultLevel) {
return false;
}
if (sourceName != null ? !sourceName.equals(jsError.sourceName)
: jsError.sourceName != null) {
return false;
}
if (!type.equals(jsError.type)) {
return false;
}
return true;
}
@Override
public int hashCode() {
// Generated by Intellij IDEA
int result = type.hashCode();
result = 31 * result + description.hashCode();
result = 31 * result + (sourceName != null ? sourceName.hashCode() : 0);
result = 31 * result + lineNumber;
result = 31 * result + defaultLevel.hashCode();
result = 31 * result + charno;
return result;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Type == null) {
propVarType = getNativeType(UNKNOWN_TYPE);
}
return propVarType;
}
return null;
}
/**
* Declares a refined type in {@code scope} for the name represented by
* {@code node}. It must be possible to refine the type of the given node in
* the given scope, as determined by {@link #getTypeIfRefinable}.
*/
protected void declareNameInScope(FlowScope scope, Node node, JSType type) {
switch (node.getType()) {
case Token.NAME:
scope.inferSlotType(node.getString(), type);
break;
case Token.GETPROP:
String qualifiedName = node.getQualifiedName();
Preconditions.checkNotNull(qualifiedName);
JSType origType = node.getJSType();
origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType;
scope.inferQualifiedSlot(node, qualifiedName, origType, type);
break;
case Token.THIS:
// "this" references aren't currently modeled in the CFG.
break;
default:
throw new IllegalArgumentException("Node cannot be refined. \n" +
node.toStringTree());
}
}
/**
* @see #getRestrictedWithoutUndefined(JSType)
*/
private final Visitor<JSType> restrictUndefinedVisitor =
new Visitor<JSType>() {
@Override
public JSType caseEnumElementType(EnumElementType enumElementType) {
JSType type = enumElementType.getPrimitiveType().visit(this);
if (type != null && enumElementType.getPrimitiveType().isEquivalentTo(type)) {
return enumElementType;
} else {
return type;
}
}
@Override
public JSType caseAllType() {
return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE,
STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE);
}
@Override
public JSType caseNoObjectType() {
return getNativeType(NO_OBJECT_TYPE);
}
@Override
public JSType caseNoType() {
return getNativeType(NO_TYPE);
}
@Override
public JSType caseBooleanType() {
return getNativeType(BOOLEAN_TYPE);
}
@Override
public JSType caseFunctionType(FunctionType type) {
return type;
}
@Override
public JSType caseNullType() {
return getNativeType(NULL_TYPE);
}
@Override
public JSType caseNumberType() {
return getNativeType(NUMBER_TYPE);
}
@Override
public JSType caseObjectType(ObjectType type) {
return type;
}
@Override
public JSType caseStringType() {
return getNativeType(STRING_TYPE);
}
@Override
public JSType caseUnionType(UnionType type) {
return type.getRestrictedUnion(getNativeType(VOID_TYPE));
}
@Override
public JSType caseUnknownType() {
return getNativeType(UNKNOWN_TYPE);
}
@Override
public JSType caseVoidType() {
return null;
}
@Override
public JSType caseTemplatizedType(TemplatizedType type) {
return caseObjectType(type);
}
@Override
public JSType caseTemplateType(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>TemplateType templateType) {
return caseObjectType(templateType);
}
};
/**
* @see #getRestrictedWithoutNull(JSType)
*/
private final Visitor<JSType> restrictNullVisitor =
new Visitor<JSType>() {
@Override
public JSType caseEnumElementType(EnumElementType enumElementType) {
JSType type = enumElementType.getPrimitiveType().visit(this);
if (type != null &&
enumElementType.getPrimitiveType().isEquivalentTo(type)) {
return enumElementType;
} else {
return type;
}
}
@Override
public JSType caseAllType() {
return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE,
STRING_TYPE, BOOLEAN_TYPE, VOID_TYPE);
}
@Override
public JSType caseNoObjectType() {
return getNativeType(NO_OBJECT_TYPE);
}
@Override
public JSType caseNoType() {
return getNativeType(NO_TYPE);
}
@Override
public JSType caseBooleanType() {
return getNativeType(BOOLEAN_TYPE);
}
@Override
public JSType caseFunctionType(FunctionType type) {
return type;
}
@Override
public JSType caseNullType() {
return null;
}
@Override
public JSType caseNumberType() {
return getNativeType(NUMBER_TYPE);
}
@Override
public JSType caseObjectType(ObjectType type) {
return type;
}
@Override
public JSType caseStringType() {
return getNativeType(STRING_TYPE);
}
@Override
public JSType caseUnionType(UnionType type) {
return type.getRestrictedUnion(getNativeType(NULL_TYPE));
}
@Override
public JSType caseUnknownType() {
return getNativeType(UNKNOWN_TYPE);
}
@Override
public JSType caseVoidType() {
return getNativeType(VOID_TYPE);
}
@Override
public JSType caseTemplatizedType(TemplatizedType type) {
return caseObjectType(type);
}
@Override
public JSType caseTemplateType(TemplateType templateType) {
return caseObjectType(templateType);
}
};
/**
* A class common to all visitors that need to restrict the type based on
* {@code typeof}-like conditions.
*/
abstract class RestrictByTypeOfResultVisitor
implements Visitor<JSType> {
/**
* Abstracts away the similarities between visiting the unknown type and the
* all type.
* @param topType {@code UNKNOWN_TYPE} or {@code ALL_TYPE}
* @return the restricted type
* @see #caseAllType
* @see #caseUnknownType
*/
protected abstract JSType caseTopType(JSType topType);
@Override
public JSType caseAllType() {
return caseTopType(getNativeType(ALL_TYPE));
}
@Override
public JSType caseUnknownType() {
return caseTopType(getNativeType(CHECKED_UNKNOWN_TYPE));
}
@Override
public JSType caseUnionType(UnionType type) {
JSType restricted = null;
for (JSType alternate : type.getAlternates()) {
JSType restrictedAlternate = alternate.visit(this);
if
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> (restrictedAlternate != null) {
if (restricted == null) {
restricted = restrictedAlternate;
} else {
restricted = restrictedAlternate.getLeastSupertype(restricted);
}
}
}
return restricted;
}
@Override
public JSType caseNoType() {
return getNativeType(NO_TYPE);
}
@Override
public JSType caseEnumElementType(EnumElementType enumElementType) {
// NOTE(nicksantos): This is a white lie. Suppose we have:
// /** @enum {string|number} */ var MyEnum = ...;
// if (goog.isNumber(myEnumInstance)) {
// /* what is myEnumInstance here? */
// }
// There is no type that represents {MyEnum - string}. What we really
// need is a notion of "enum subtyping", so that we could dynamically
// create a subtype of MyEnum restricted by string. In any case,
// this should catch the common case.
JSType type = enumElementType.getPrimitiveType().visit(this);
if (type != null &&
enumElementType.getPrimitiveType().isEquivalentTo(type)) {
return enumElementType;
} else {
return type;
}
}
@Override
public JSType caseTemplatizedType(TemplatizedType type) {
return caseObjectType(type);
}
@Override
public JSType caseTemplateType(TemplateType templateType) {
return caseObjectType(templateType);
}
}
/**
* A class common to all visitors that need to restrict the type based on
* some {@code typeof}-like condition being true. All base cases return
* {@code null}. It is up to the subclasses to override the appropriate ones.
*/
abstract class RestrictByTrueTypeOfResultVisitor
extends RestrictByTypeOfResultVisitor {
@Override
public JSType caseNoObjectType() {
return null;
}
@Override
public JSType caseBooleanType() {
return null;
}
@Override
public JSType caseFunctionType(FunctionType type) {
return null;
}
@Override
public JSType caseNullType() {
return null;
}
@Override
public JSType caseNumberType() {
return null;
}
@Override
public JSType caseObjectType(ObjectType type) {
return null;
}
@Override
public JSType caseStringType() {
return null;
}
@Override
public JSType caseVoidType() {
return null;
}
}
/**
* A class common to all visitors that need to restrict the type based on
* some {@code typeof}-like condition being false. All base cases return
* their type. It is up to the subclasses to override the appropriate ones.
*/
abstract class RestrictByFalseTypeOfResultVisitor
extends RestrictByTypeOfResultVisitor {
@Override
protected JSType caseTopType(JSType topType) {
return topType;
}
@Override
public JSType caseNoObjectType() {
return getNativeType(NO_OBJECT_TYPE);
}
@Override
public JSType caseBooleanType() {
return getNativeType(BOOLEAN_TYPE);
}
@Override
public JSType caseFunctionType(FunctionType type) {
return type;
}
@Override
public JSType caseNull
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Type() {
return getNativeType(NULL_TYPE);
}
@Override
public JSType caseNumberType() {
return getNativeType(NUMBER_TYPE);
}
@Override
public JSType caseObjectType(ObjectType type) {
return type;
}
@Override
public JSType caseStringType() {
return getNativeType(STRING_TYPE);
}
@Override
public JSType caseVoidType() {
return getNativeType(VOID_TYPE);
}
}
/**
* @see ChainableReverseAbstractInterpreter#getRestrictedByTypeOfResult
*/
private class RestrictByOneTypeOfResultVisitor
extends RestrictByTypeOfResultVisitor {
/**
* A value known to be equal or not equal to the result of the
* {@code typeOf} operation.
*/
private final String value;
/**
* {@code true} if the {@code typeOf} result is known to equal
* {@code value}; {@code false} if it is known <em>not</em> to equal
* {@code value}.
*/
private final boolean resultEqualsValue;
RestrictByOneTypeOfResultVisitor(String value, boolean resultEqualsValue) {
this.value = value;
this.resultEqualsValue = resultEqualsValue;
}
/**
* Computes whether the given result of a {@code typeof} operator matches
* expectations, i.e. whether a type that gives such a result should be
* kept.
*/
private boolean matchesExpectation(String result) {
return result.equals(value) == resultEqualsValue;
}
@Override
protected JSType caseTopType(JSType topType) {
JSType result = topType;
if (resultEqualsValue) {
JSType typeByName = getNativeTypeForTypeOf(value);
if (typeByName != null) {
result = typeByName;
}
}
return result;
}
@Override
public JSType caseNoObjectType() {
return (value.equals("object") || value.equals("function")) ==
resultEqualsValue ? getNativeType(NO_OBJECT_TYPE) : null;
}
@Override
public JSType caseBooleanType() {
return matchesExpectation("boolean") ? getNativeType(BOOLEAN_TYPE) : null;
}
@Override
public JSType caseFunctionType(FunctionType type) {
return matchesExpectation("function") ? type : null;
}
@Override
public JSType caseNullType() {
return matchesExpectation("object") ? getNativeType(NULL_TYPE) : null;
}
@Override
public JSType caseNumberType() {
return matchesExpectation("number") ? getNativeType(NUMBER_TYPE) : null;
}
@Override
public JSType caseObjectType(ObjectType type) {
if (value.equals("function")) {
JSType ctorType = getNativeType(U2U_CONSTRUCTOR_TYPE);
if (resultEqualsValue) {
// Objects are restricted to "Function", subtypes are left
return ctorType.getGreatestSubtype(type);
} else {
// Only filter out subtypes of "function"
return type.isSubtype(ctorType) ? null : type;
}
}
return matchesExpectation("object") ? type : null;
}
@Override
public JSType caseStringType() {
return matches
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Expectation("string") ? getNativeType(STRING_TYPE) : null;
}
@Override
public JSType caseVoidType() {
return matchesExpectation("undefined") ? getNativeType(VOID_TYPE) : null;
}
}
/**
* Returns a version of type where undefined is not present.
*/
protected final JSType getRestrictedWithoutUndefined(JSType type) {
return type == null ? null : type.visit(restrictUndefinedVisitor);
}
/**
* Returns a version of type where null is not present.
*/
protected final JSType getRestrictedWithoutNull(JSType type) {
return type == null ? null : type.visit(restrictNullVisitor);
}
/**
* Returns a version of {@code type} that is restricted by some knowledge
* about the result of the {@code typeof} operation.
* <p>
* The behavior of the {@code typeof} operator can be summarized by the
* following table:
* <table>
* <tr><th>type</th><th>result</th></tr>
* <tr><td>{@code undefined}</td><td>"undefined"</td></tr>
* <tr><td>{@code null}</td><td>"object"</td></tr>
* <tr><td>{@code boolean}</td><td>"boolean"</td></tr>
* <tr><td>{@code number}</td><td>"number"</td></tr>
* <tr><td>{@code string}</td><td>"string"</td></tr>
* <tr><td>{@code Object} (which doesn't implement [[Call]])</td>
* <td>"object"</td></tr>
* <tr><td>{@code Object} (which implements [[Call]])</td>
* <td>"function"</td></tr>
* </table>
* @param type the type to restrict
* @param value A value known to be equal or not equal to the result of the
* {@code typeof} operation
* @param resultEqualsValue {@code true} if the {@code typeOf} result is known
* to equal {@code value}; {@code false} if it is known <em>not</em> to
* equal {@code value}
* @return the restricted type or null if no version of the type matches the
* restriction
*/
JSType getRestrictedByTypeOfResult(JSType type, String value,
boolean resultEqualsValue) {
if (type == null) {
if (resultEqualsValue) {
JSType result = getNativeTypeForTypeOf(value);
return result == null ? getNativeType(CHECKED_UNKNOWN_TYPE) : result;
} else {
return null;
}
}
return type.visit(
new RestrictByOneTypeOfResultVisitor(value, resultEqualsValue));
}
JSType getNativeType(JSTypeNative typeId) {
return typeRegistry.getNativeType(typeId);
}
/**
* If we definitely know what a type is based on the typeof result,
* return it. Otherwise, return null.
*
* The typeof operation in JS is poorly defined, and this function works
* for both the native typeof and goog.typeOf. It should not be made public,
* because its semantics are informally defined
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2010 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Maps;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.Map;
/**
* Filters warnings based on in-code {@code @suppress} annotations.
* @author nicksantos@google.com (Nick Santos)
*/
class SuppressDocWarningsGuard extends WarningsGuard {
private static final long serialVersionUID = 1L;
/** Warnings guards for each suppressible warnings group, indexed by name. */
private final Map<String, DiagnosticGroupWarningsGuard> suppressors =
Maps.newHashMap();
/**
* The suppressible groups, indexed by name.
*/
SuppressDocWarningsGuard(Map<String, DiagnosticGroup> suppressibleGroups) {
for (Map.Entry<String, DiagnosticGroup> entry :
suppressibleGroups.entrySet()) {
suppressors.put(
entry.getKey(),
new DiagnosticGroupWarningsGuard(
entry.getValue(),
CheckLevel.OFF));
}
}
@Override
public CheckLevel level(JSError error) {
Node node = error.node;
if (node != null) {
boolean visitedFunction = false;
for (Node current = node;
current != null;
current = current.getParent()) {
int type = current.getType();
JSDocInfo info = null;
if (type == Token.FUNCTION) {
info = NodeUtil.getBestJSDocInfo(current);
visitedFunction = true;
} else if (type == Token.SCRIPT) {
info = current.getJSDocInfo();
} else if (current.isVar() || current.isAssign()) {
// There's one edge case we're worried about:
// if the warning points to an assigment to a function, we
// want the suppressions on that function to apply.
// It's OK if we double-count some cases.
Node rhs = NodeUtil.getRValueOfLValue(current.getFirstChild());
if (rhs != null) {
if (rhs.isCast()) {
rhs = rhs.getFirstChild();
}
if (rhs.isFunction() && !visitedFunction) {
info = NodeUtil.getBestJSDocInfo(current);
}
}
}
if (info != null) {
for (String suppressor : info.getSuppressions()) {
WarningsGuard guard = suppressors.get(suppressor);
// Some @suppress
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> tags are for other tools, and
// may not have a warnings guard.
if (guard != null) {
CheckLevel newLevel = guard.level(error);
if (newLevel != null) {
return newLevel;
}
}
}
}
}
}
return null;
}
@Override
public int getPriority() {
// Happens after path-based filtering, but before other times
// of filtering.
return WarningsGuard.Priority.SUPPRESS_DOC.value;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>NaN;
}
if (name.equals("Infinity")) {
return Double.POSITIVE_INFINITY;
}
return null;
case Token.NEG:
if (n.getChildCount() == 1 && n.getFirstChild().isName()
&& n.getFirstChild().getString().equals("Infinity")) {
return Double.NEGATIVE_INFINITY;
}
return null;
case Token.NOT:
TernaryValue child = getPureBooleanValue(n.getFirstChild());
if (child != TernaryValue.UNKNOWN) {
return child.toBoolean(true) ? 0.0 : 1.0; // reversed.
}
break;
case Token.STRING:
return getStringNumberValue(n.getString());
case Token.ARRAYLIT:
case Token.OBJECTLIT:
String value = getStringValue(n);
return value != null ? getStringNumberValue(value) : null;
}
return null;
}
static Double getStringNumberValue(String rawJsString) {
if (rawJsString.contains("\u000b")) {
// vertical tab is not always whitespace
return null;
}
String s = trimJsWhiteSpace(rawJsString);
// return ScriptRuntime.toNumber(s);
if (s.length() == 0) {
return 0.0;
}
if (s.length() > 2
&& s.charAt(0) == '0'
&& (s.charAt(1) == 'x' || s.charAt(1) == 'X')) {
// Attempt to convert hex numbers.
try {
return Double.valueOf(Integer.parseInt(s.substring(2), 16));
} catch (NumberFormatException e) {
return Double.NaN;
}
}
if (s.length() > 3
&& (s.charAt(0) == '-' || s.charAt(0) == '+')
&& s.charAt(1) == '0'
&& (s.charAt(2) == 'x' || s.charAt(2) == 'X')) {
// hex numbers with explicit signs vary between browsers.
return null;
}
// Firefox and IE treat the "Infinity" differently. Firefox is case
// insensitive, but IE treats "infinity" as NaN. So leave it alone.
if (s.equals("infinity")
|| s.equals("-infinity")
|| s.equals("+infinity")) {
return null;
}
try {
return Double.parseDouble(s);
} catch (NumberFormatException e) {
return Double.NaN;
}
}
static String trimJsWhiteSpace(String s) {
int start = 0;
int end = s.length();
while (end > 0
&& isStrWhiteSpaceChar(s.charAt(end - 1)) == TernaryValue.TRUE) {
end--;
}
while (start < end
&& isStrWhiteSpaceChar(s.charAt(start)) == TernaryValue.TRUE) {
start++;
}
return s.substring(start, end);
}
/**
* Copied from Rhino's ScriptRuntime
*/
public static TernaryValue isStrWhiteSpaceChar(int c) {
switch (c) {
case '\u000B': // <VT>
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> any captured variables directly, it would still fail this definition,
* because it affects the lifecycle of variables in the enclosing scope.
*
* However, a function literal with respect to a particular scope is
* a literal.
*
* @param includeFunctions If true, all function expressions will be
* treated as literals.
*/
static boolean isLiteralValue(Node n, boolean includeFunctions) {
switch (n.getType()) {
case Token.CAST:
return isLiteralValue(n.getFirstChild(), includeFunctions);
case Token.ARRAYLIT:
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if ((!child.isEmpty()) && !isLiteralValue(child, includeFunctions)) {
return false;
}
}
return true;
case Token.REGEXP:
// Return true only if all children are const.
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if (!isLiteralValue(child, includeFunctions)) {
return false;
}
}
return true;
case Token.OBJECTLIT:
// Return true only if all values are const.
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if (!isLiteralValue(child.getFirstChild(), includeFunctions)) {
return false;
}
}
return true;
case Token.FUNCTION:
return includeFunctions && !NodeUtil.isFunctionDeclaration(n);
default:
return isImmutableValue(n);
}
}
/**
* Determines whether the given value may be assigned to a define.
*
* @param val The value being assigned.
* @param defines The list of names of existing defines.
*/
static boolean isValidDefineValue(Node val, Set<String> defines) {
switch (val.getType()) {
case Token.STRING:
case Token.NUMBER:
case Token.TRUE:
case Token.FALSE:
return true;
// Binary operators are only valid if both children are valid.
case Token.ADD:
case Token.BITAND:
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GT:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.URSH:
return isValidDefineValue(val.getFirstChild(), defines)
&& isValidDefineValue(val.getLastChild(), defines);
// Unary operators are valid if the child is valid.
case Token.NOT:
case Token.NEG:
case Token.POS:
return isValidDefineValue(val.getFirstChild(), defines);
// Names are valid if and only if they are defines themselves.
case Token.NAME:
case Token.GETPROP:
if (val.isQualifiedName()) {
return defines.contains(val.getQualifiedName());
}
}
return false;
}
/**
* Returns whether this a BLOCK node with no children.
*
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Target.getFirstChild();
if (evaluatesToLocalValue(current)) {
return false;
}
// A literal value as defined by "isLiteralValue" is guaranteed
// not to be an alias, or any components which are aliases of
// other objects.
// If the root object is a literal don't consider this a
// side-effect.
while (isGet(current)) {
current = current.getFirstChild();
}
return !isLiteralValue(current, true);
} else {
// TODO(johnlenz): remove this code and make this an exception. This
// is here only for legacy reasons, the AST is not valid but
// preserve existing behavior.
return !isLiteralValue(assignTarget, true);
}
}
return true;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) {
return true;
}
}
return false;
}
/**
* Do calls to this constructor have side effects?
*
* @param callNode - constructor call node
*/
static boolean constructorCallHasSideEffects(Node callNode) {
return constructorCallHasSideEffects(callNode, null);
}
static boolean constructorCallHasSideEffects(
Node callNode, AbstractCompiler compiler) {
if (!callNode.isNew()) {
throw new IllegalStateException(
"Expected NEW node, got " + Token.name(callNode.getType()));
}
if (callNode.isNoSideEffectsCall()) {
return false;
}
if (callNode.isOnlyModifiesArgumentsCall() &&
allArgsUnescapedLocal(callNode)) {
return false;
}
Node nameNode = callNode.getFirstChild();
if (nameNode.isName() &&
CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) {
return false;
}
return true;
}
// A list of built-in object creation or primitive type cast functions that
// can also be called as constructors but lack side-effects.
// TODO(johnlenz): consider adding an extern annotation for this.
private static final Set<String> BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS =
ImmutableSet.of(
"Object", "Array", "String", "Number", "Boolean", "RegExp", "Error");
private static final Set<String> OBJECT_METHODS_WITHOUT_SIDEEFFECTS =
ImmutableSet.of("toString", "valueOf");
private static final Set<String> REGEXP_METHODS =
ImmutableSet.of("test", "exec");
private static final Set<String> STRING_REGEXP_METHODS =
ImmutableSet.of("match", "replace", "search", "split");
/**
* Returns true if calls to this function have side effects.
*
* @param callNode - function call node
*/
static boolean functionCallHasSideEffects(Node callNode) {
return functionCallHasSideEffects(callNode, null);
}
/**
* Returns true if calls to this function have side effects.
*
* @param callNode The call node to inspected.
* @param compiler A compiler object to provide program state changing
*
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> context information. Can be null.
*/
static boolean functionCallHasSideEffects(
Node callNode, @Nullable AbstractCompiler compiler) {
if (!callNode.isCall()) {
throw new IllegalStateException(
"Expected CALL node, got " + Token.name(callNode.getType()));
}
if (callNode.isNoSideEffectsCall()) {
return false;
}
if (callNode.isOnlyModifiesArgumentsCall() &&
allArgsUnescapedLocal(callNode)) {
return false;
}
Node nameNode = callNode.getFirstChild();
// Built-in functions with no side effects.
if (nameNode.isName()) {
String name = nameNode.getString();
if (BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS.contains(name)) {
return false;
}
} else if (nameNode.isGetProp()) {
if (callNode.hasOneChild()
&& OBJECT_METHODS_WITHOUT_SIDEEFFECTS.contains(
nameNode.getLastChild().getString())) {
return false;
}
if (callNode.isOnlyModifiesThisCall()
&& evaluatesToLocalValue(nameNode.getFirstChild())) {
return false;
}
// Math.floor has no side-effects.
// TODO(nicksantos): This is a terrible terrible hack, until
// I create a definitionProvider that understands namespacing.
if (nameNode.getFirstChild().isName()) {
if ("Math.floor".equals(nameNode.getQualifiedName())) {
return false;
}
}
if (compiler != null && !compiler.hasRegExpGlobalReferences()) {
if (nameNode.getFirstChild().isRegExp()
&& REGEXP_METHODS.contains(nameNode.getLastChild().getString())) {
return false;
} else if (nameNode.getFirstChild().isString()
&& STRING_REGEXP_METHODS.contains(
nameNode.getLastChild().getString())) {
Node param = nameNode.getNext();
if (param != null &&
(param.isString() || param.isRegExp())) {
return false;
}
}
}
}
return true;
}
/**
* @return Whether the call has a local result.
*/
static boolean callHasLocalResult(Node n) {
Preconditions.checkState(n.isCall());
return (n.getSideEffectFlags() & Node.FLAG_LOCAL_RESULTS) > 0;
}
/**
* @return Whether the new has a local result.
*/
static boolean newHasLocalResult(Node n) {
Preconditions.checkState(n.isNew());
return n.isOnlyModifiesThisCall();
}
/**
* Returns true if the current node's type implies side effects.
*
* This is a non-recursive version of the may have side effects
* check; used to check wherever the current node's type is one of
* the reason's why a subtree has side effects.
*/
static boolean nodeTypeMayHaveSideEffects(Node n) {
return nodeTypeMayHaveSideEffects(n, null);
}
static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) {
if (isAssignmentOp(n)) {
return true;
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> switch(n.getType()) {
case Token.DELPROP:
case Token.DEC:
case Token.INC:
case Token.THROW:
return true;
case Token.CALL:
return NodeUtil.functionCallHasSideEffects(n, compiler);
case Token.NEW:
return NodeUtil.constructorCallHasSideEffects(n, compiler);
case Token.NAME:
// A variable definition.
return n.hasChildren();
default:
return false;
}
}
static boolean allArgsUnescapedLocal(Node callOrNew) {
for (Node arg = callOrNew.getFirstChild().getNext();
arg != null; arg = arg.getNext()) {
if (!evaluatesToLocalValue(arg)) {
return false;
}
}
return true;
}
/**
* @return Whether the tree can be affected by side-effects or
* has side-effects.
*/
static boolean canBeSideEffected(Node n) {
Set<String> emptySet = Collections.emptySet();
return canBeSideEffected(n, emptySet);
}
/**
* @param knownConstants A set of names known to be constant value at
* node 'n' (such as locals that are last written before n can execute).
* @return Whether the tree can be affected by side-effects or
* has side-effects.
*/
static boolean canBeSideEffected(Node n, Set<String> knownConstants) {
switch (n.getType()) {
case Token.CALL:
case Token.NEW:
// Function calls or constructor can reference changed values.
// TODO(johnlenz): Add some mechanism for determining that functions
// are unaffected by side effects.
return true;
case Token.NAME:
// Non-constant names values may have been changed.
return !isConstantName(n)
&& !knownConstants.contains(n.getString());
// Properties on constant NAMEs can still be side-effected.
case Token.GETPROP:
case Token.GETELEM:
return true;
case Token.FUNCTION:
// Function expression are not changed by side-effects,
// and function declarations are not part of expressions.
Preconditions.checkState(isFunctionExpression(n));
return false;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (canBeSideEffected(c, knownConstants)) {
return true;
}
}
return false;
}
/*
* 0 comma ,
* 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |=
* 2 conditional ?:
* 3 logical-or ||
* 4 logical-and &&
* 5 bitwise-or |
* 6 bitwise-xor ^
* 7 bitwise-and &
* 8 equality == !=
* 9 relational < <= > >=
* 10 bitwise shift << >> >>>
* 11 addition/subtraction + -
* 12 multiply/divide * / %
* 13 negation/increment ! ~ - ++ --
* 14 call, member () [] .
*/
static int precedence(int type) {
int precedence = precedenceWithDefault(type);
if (precedence !=
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> -1) {
return precedence;
}
throw new Error("Unknown precedence for " +
Token.name(type) + " (type " + type + ")");
}
static int precedenceWithDefault(int type) {
switch (type) {
case Token.COMMA: return 0;
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN: return 1;
case Token.HOOK: return 2; // ?: operator
case Token.OR: return 3;
case Token.AND: return 4;
case Token.BITOR: return 5;
case Token.BITXOR: return 6;
case Token.BITAND: return 7;
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE: return 8;
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
case Token.INSTANCEOF:
case Token.IN: return 9;
case Token.LSH:
case Token.RSH:
case Token.URSH: return 10;
case Token.SUB:
case Token.ADD: return 11;
case Token.MUL:
case Token.MOD:
case Token.DIV: return 12;
case Token.INC:
case Token.DEC:
case Token.NEW:
case Token.DELPROP:
case Token.TYPEOF:
case Token.VOID:
case Token.NOT:
case Token.BITNOT:
case Token.POS:
case Token.NEG: return 13;
case Token.CALL:
case Token.GETELEM:
case Token.GETPROP:
// Data values
case Token.ARRAYLIT:
case Token.EMPTY: // TODO(johnlenz): remove this.
case Token.FALSE:
case Token.FUNCTION:
case Token.NAME:
case Token.NULL:
case Token.NUMBER:
case Token.OBJECTLIT:
case Token.REGEXP:
case Token.STRING:
case Token.STRING_KEY:
case Token.THIS:
case Token.TRUE:
return 15;
case Token.CAST:
return 16;
default:
// Statements are lower precedence than expressions.
return -1;
}
}
static boolean isUndefined(Node n) {
switch (n.getType()) {
case Token.VOID:
return true;
case Token.NAME:
return n.getString().equals("undefined");
}
return false;
}
static boolean isNullOrUndefined(Node n) {
return n.isNull() || isUndefined(n);
}
static final Predicate<Node> IMMUTABLE_PREDICATE = new Predicate<Node>() {
@Override
public boolean apply(Node n) {
return isImmutableValue(n
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>);
}
};
static boolean isImmutableResult(Node n) {
return allResultsMatch(n, IMMUTABLE_PREDICATE);
}
/**
* Apply the supplied predicate against
* all possible result Nodes of the expression.
*/
static boolean allResultsMatch(Node n, Predicate<Node> p) {
switch (n.getType()) {
case Token.CAST:
return allResultsMatch(n.getFirstChild(), p);
case Token.ASSIGN:
case Token.COMMA:
return allResultsMatch(n.getLastChild(), p);
case Token.AND:
case Token.OR:
return allResultsMatch(n.getFirstChild(), p)
&& allResultsMatch(n.getLastChild(), p);
case Token.HOOK:
return allResultsMatch(n.getFirstChild().getNext(), p)
&& allResultsMatch(n.getLastChild(), p);
default:
return p.apply(n);
}
}
/**
* Apply the supplied predicate against
* all possible result Nodes of the expression.
*/
static boolean anyResultsMatch(Node n, Predicate<Node> p) {
switch (n.getType()) {
case Token.CAST:
return anyResultsMatch(n.getFirstChild(), p);
case Token.ASSIGN:
case Token.COMMA:
return anyResultsMatch(n.getLastChild(), p);
case Token.AND:
case Token.OR:
return anyResultsMatch(n.getFirstChild(), p)
|| anyResultsMatch(n.getLastChild(), p);
case Token.HOOK:
return anyResultsMatch(n.getFirstChild().getNext(), p)
|| anyResultsMatch(n.getLastChild(), p);
default:
return p.apply(n);
}
}
static class NumbericResultPredicate implements Predicate<Node> {
@Override
public boolean apply(Node n) {
return isNumericResultHelper(n);
}
}
static final NumbericResultPredicate NUMBERIC_RESULT_PREDICATE =
new NumbericResultPredicate();
/**
* Returns true if the result of node evaluation is always a number
*/
static boolean isNumericResult(Node n) {
return allResultsMatch(n, NUMBERIC_RESULT_PREDICATE);
}
static boolean isNumericResultHelper(Node n) {
switch (n.getType()) {
case Token.ADD:
return !mayBeString(n.getFirstChild())
&& !mayBeString(n.getLastChild());
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.SUB:
case Token.MUL:
case Token.MOD:
case Token.DIV:
case Token.INC:
case Token.DEC:
case Token.POS:
case Token.NEG:
case Token.NUMBER:
return true;
case Token.NAME:
String name = n.getString();
if (name.equals("NaN")) {
return true;
}
if (name.equals("Infinity")) {
return true;
}
return false;
default:
return false;
}
}
static class BooleanResultPredicate implements Predicate<Node> {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
@Override
public boolean apply(Node n) {
return isBooleanResultHelper(n);
}
}
static final BooleanResultPredicate BOOLEAN_RESULT_PREDICATE =
new BooleanResultPredicate();
/**
* @return Whether the result of node evaluation is always a boolean
*/
static boolean isBooleanResult(Node n) {
return allResultsMatch(n, BOOLEAN_RESULT_PREDICATE);
}
static boolean isBooleanResultHelper(Node n) {
switch (n.getType()) {
// Primitives
case Token.TRUE:
case Token.FALSE:
// Comparisons
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE:
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
// Queries
case Token.IN:
case Token.INSTANCEOF:
// Inversion
case Token.NOT:
// delete operator returns a boolean.
case Token.DELPROP:
return true;
default:
return false;
}
}
static class MayBeStringResultPredicate implements Predicate<Node> {
@Override
public boolean apply(Node n) {
return mayBeStringHelper(n);
}
}
static final MayBeStringResultPredicate MAY_BE_STRING_PREDICATE =
new MayBeStringResultPredicate();
/**
* @returns Whether the results is possibly a string.
*/
static boolean mayBeString(Node n) {
return mayBeString(n, true);
}
static boolean mayBeString(Node n, boolean recurse) {
if (recurse) {
return anyResultsMatch(n, MAY_BE_STRING_PREDICATE);
} else {
return mayBeStringHelper(n);
}
}
static boolean mayBeStringHelper(Node n) {
return !isNumericResult(n) && !isBooleanResult(n)
&& !isUndefined(n) && !n.isNull();
}
/**
* Returns true if the operator is associative.
* e.g. (a * b) * c = a * (b * c)
* Note: "+" is not associative because it is also the concatenation
* for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2
*/
static boolean isAssociative(int type) {
switch (type) {
case Token.MUL:
case Token.AND:
case Token.OR:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
return true;
default:
return false;
}
}
/**
* Returns true if the operator is commutative.
* e.g. (a * b) * c = c * (b * a)
* Note 1: "+" is not commutative because it is also the concatenation
* for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2
* Note 2: only operations on literals and pure functions are commutative.
*/
static boolean isCommutative(int type) {
switch (type) {
case Token.MUL:
case
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> * Determines whether a node represents an object literal key
* (e.g. key1 in {key1: value1, key2: value2}).
*
* @param node A node
*/
static boolean isObjectLitKey(Node node) {
switch (node.getType()) {
case Token.STRING_KEY:
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return true;
}
return false;
}
/**
* Get the name of an object literal key.
*
* @param key A node
*/
static String getObjectLitKeyName(Node key) {
switch (key.getType()) {
case Token.STRING_KEY:
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return key.getString();
}
throw new IllegalStateException("Unexpected node type: " + key);
}
/**
* @param key A OBJECTLIT key node.
* @return The type expected when using the key.
*/
static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) {
if (valueType != null) {
switch (key.getType()) {
case Token.GETTER_DEF:
// GET must always return a function type.
if (valueType.isFunctionType()) {
FunctionType fntype = valueType.toMaybeFunctionType();
valueType = fntype.getReturnType();
} else {
return null;
}
break;
case Token.SETTER_DEF:
if (valueType.isFunctionType()) {
// SET must always return a function type.
FunctionType fntype = valueType.toMaybeFunctionType();
Node param = fntype.getParametersNode().getFirstChild();
// SET function must always have one parameter.
valueType = param.getJSType();
} else {
return null;
}
break;
}
}
return valueType;
}
/**
* Determines whether a node represents an object literal get or set key
* (e.g. key1 in {get key1() {}, set key2(a){}).
*
* @param node A node
*/
static boolean isGetOrSetKey(Node node) {
switch (node.getType()) {
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return true;
}
return false;
}
/**
* Converts an operator's token value (see {@link Token}) to a string
* representation.
*
* @param operator the operator's token value to convert
* @return the string representation or {@code null} if the token value is
* not an operator
*/
static String opToStr(int operator) {
switch (operator) {
case Token.BITOR: return "|";
case Token.OR: return "||";
case Token.BITXOR: return "^";
case Token.AND: return "&&";
case Token.BITAND: return "&";
case Token.SHEQ: return "===";
case Token.EQ: return "==";
case Token.NOT: return "!";
case Token.NE: return "!=";
case Token.SHNE: return "!==";
case Token.LSH: return "<<";
case Token.IN: return "in";
case Token.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>LE: return "<=";
case Token.LT: return "<";
case Token.URSH: return ">>>";
case Token.RSH: return ">>";
case Token.GE: return ">=";
case Token.GT: return ">";
case Token.MUL: return "*";
case Token.DIV: return "/";
case Token.MOD: return "%";
case Token.BITNOT: return "~";
case Token.ADD: return "+";
case Token.SUB: return "-";
case Token.POS: return "+";
case Token.NEG: return "-";
case Token.ASSIGN: return "=";
case Token.ASSIGN_BITOR: return "|=";
case Token.ASSIGN_BITXOR: return "^=";
case Token.ASSIGN_BITAND: return "&=";
case Token.ASSIGN_LSH: return "<<=";
case Token.ASSIGN_RSH: return ">>=";
case Token.ASSIGN_URSH: return ">>>=";
case Token.ASSIGN_ADD: return "+=";
case Token.ASSIGN_SUB: return "-=";
case Token.ASSIGN_MUL: return "*=";
case Token.ASSIGN_DIV: return "/=";
case Token.ASSIGN_MOD: return "%=";
case Token.VOID: return "void";
case Token.TYPEOF: return "typeof";
case Token.INSTANCEOF: return "instanceof";
default: return null;
}
}
/**
* Converts an operator's token value (see {@link Token}) to a string
* representation or fails.
*
* @param operator the operator's token value to convert
* @return the string representation
* @throws Error if the token value is not an operator
*/
static String opToStrNoFail(int operator) {
String res = opToStr(operator);
if (res == null) {
throw new Error("Unknown op " + operator + ": " +
Token.name(operator));
}
return res;
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node,
int type,
Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node, int type) {
return containsType(node, type, Predicates.<Node>alwaysTrue());
}
/**
* Given a node tree, finds all the VAR declarations in that tree that are
* not in an inner scope. Then adds a new VAR node at the top of the current
* scope that redeclares them, if necessary.
*/
static void redeclareVarsInsideBranch(Node branch) {
Collection<Node> vars = getVarsDeclaredInBranch(branch);
if (vars.isEmpty()) {
return;
}
Node parent = getAddingRoot(branch);
for (Node nameNode : vars) {
Node var = IR.var(
IR.name(nameNode.getString())
.srcref(nameNode))
.srcref(nameNode);
copyNameAnnotations(nameNode, var.getFirstChild());
parent.addChildToFront(var);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
Node nameNode = newName(convention, name, basisNode);
nameNode.putProp(Node.ORIGINALNAME_PROP, originalName);
return nameNode;
}
/** Test if all characters in the string are in the Basic Latin (aka ASCII)
* character set - that they have UTF-16 values equal to or below 0x7f.
* This check can find which identifiers with Unicode characters need to be
* escaped in order to allow resulting files to be processed by non-Unicode
* aware UNIX tools and editors.
* *
* See http://en.wikipedia.org/wiki/Latin_characters_in_Unicode
* for more on Basic Latin.
*
* @param s The string to be checked for ASCII-goodness.
*
* @return True if all characters in the string are in Basic Latin set.
*/
static boolean isLatin(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c > LARGEST_BASIC_LATIN) {
return false;
}
}
return true;
}
/**
* Determines whether the given name is a valid variable name.
*/
static boolean isValidSimpleName(String name) {
return TokenStream.isJSIdentifier(name) &&
!TokenStream.isKeyword(name) &&
// no Unicode escaped characters - some browsers are less tolerant
// of Unicode characters that might be valid according to the
// language spec.
// Note that by this point, Unicode escapes have been converted
// to UTF-16 characters, so we're only searching for character
// values, not escapes.
isLatin(name);
}
/**
* Determines whether the given name is a valid qualified name.
*/
// TODO(nicksantos): This should be moved into a "Language" API,
// so that the results are different for es5 and es3.
public static boolean isValidQualifiedName(String name) {
if (name.endsWith(".") || name.startsWith(".")) {
return false;
}
String[] parts = name.split("\\.");
for (String part : parts) {
if (!isValidSimpleName(part)) {
return false;
}
}
return true;
}
/**
* Determines whether the given name can appear on the right side of
* the dot operator. Many properties (like reserved words) cannot.
*/
static boolean isValidPropertyName(String name) {
return isValidSimpleName(name);
}
private static class VarCollector implements Visitor {
final Map<String, Node> vars = Maps.newLinkedHashMap();
@Override
public void visit(Node n) {
if (n.isName()) {
Node parent = n.getParent();
if (parent != null && parent.isVar()) {
String name = n.getString();
if (!vars.containsKey(name)) {
vars.put(name, n);
}
}
}
}
}
/**
* Retrieves vars declared in the current node tree, excluding descent scopes.
*/
static Collection<Node> getVarsDeclaredInBranch(Node root) {
VarCollector collector = new VarCollector();
visitPreOrder(
root,
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
collector,
MATCH_NOT_FUNCTION);
return collector.vars.values();
}
/**
* @return {@code true} if the node an assignment to a prototype property of
* some constructor.
*/
static boolean isPrototypePropertyDeclaration(Node n) {
return isExprAssign(n) &&
isPrototypeProperty(n.getFirstChild().getFirstChild());
}
/**
* @return Whether the node represents a qualified prototype property.
*/
static boolean isPrototypeProperty(Node n) {
String lhsString = n.getQualifiedName();
return lhsString != null && lhsString.contains(".prototype.");
}
/**
* @return The class name part of a qualified prototype name.
*/
static Node getPrototypeClassName(Node qName) {
Node cur = qName;
while (cur.isGetProp()) {
if (cur.getLastChild().getString().equals("prototype")) {
return cur.getFirstChild();
} else {
cur = cur.getFirstChild();
}
}
return null;
}
/**
* @return The string property name part of a qualified prototype name.
*/
static String getPrototypePropertyName(Node qName) {
String qNameStr = qName.getQualifiedName();
int prototypeIdx = qNameStr.lastIndexOf(".prototype.");
int memberIndex = prototypeIdx + ".prototype".length() + 1;
return qNameStr.substring(memberIndex);
}
/**
* Create a node for an empty result expression:
* "void 0"
*/
static Node newUndefinedNode(Node srcReferenceNode) {
Node node = IR.voidNode(IR.number(0));
if (srcReferenceNode != null) {
node.copyInformationFromForTree(srcReferenceNode);
}
return node;
}
/**
* Create a VAR node containing the given name and initial value expression.
*/
static Node newVarNode(String name, Node value) {
Node nodeName = IR.name(name);
if (value != null) {
Preconditions.checkState(value.getNext() == null);
nodeName.addChildToBack(value);
nodeName.srcref(value);
}
Node var = IR.var(nodeName).srcref(nodeName);
return var;
}
/**
* A predicate for matching name nodes with the specified node.
*/
private static class MatchNameNode implements Predicate<Node>{
final String name;
MatchNameNode(String name){
this.name = name;
}
@Override
public boolean apply(Node n) {
return n.isName() && n.getString().equals(name);
}
}
/**
* A predicate for matching nodes with the specified type.
*/
static class MatchNodeType implements Predicate<Node>{
final int type;
MatchNodeType(int type){
this.type = type;
}
@Override
public boolean apply(Node n) {
return n.getType() == type;
}
}
/**
* A predicate for matching var or function declarations.
*/
static class MatchDeclaration implements Predicate<Node> {
@Override
public boolean apply(Node n) {
return isFunctionDeclaration(n) || n.isVar();
}
}
/**
* A predicate for matching anything except function nodes.
*/
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> private static class MatchNotFunction implements Predicate<Node>{
@Override
public boolean apply(Node n) {
return !n.isFunction();
}
}
static final Predicate<Node> MATCH_NOT_FUNCTION = new MatchNotFunction();
/**
* A predicate for matching statements without exiting the current scope.
*/
static class MatchShallowStatement implements Predicate<Node>{
@Override
public boolean apply(Node n) {
Node parent = n.getParent();
return n.isBlock()
|| (!n.isFunction() && (parent == null
|| isControlStructure(parent)
|| isStatementBlock(parent)));
}
}
/**
* Finds the number of times a type is referenced within the node tree.
*/
static int getNodeTypeReferenceCount(
Node node, int type, Predicate<Node> traverseChildrenPred) {
return getCount(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* Whether a simple name is referenced within the node tree.
*/
static boolean isNameReferenced(Node node,
String name,
Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNameNode(name), traverseChildrenPred);
}
/**
* Whether a simple name is referenced within the node tree.
*/
static boolean isNameReferenced(Node node, String name) {
return isNameReferenced(node, name, Predicates.<Node>alwaysTrue());
}
/**
* Finds the number of times a simple name is referenced within the node tree.
*/
static int getNameReferenceCount(Node node, String name) {
return getCount(
node, new MatchNameNode(name), Predicates.<Node>alwaysTrue());
}
/**
* @return Whether the predicate is true for the node or any of its children.
*/
static boolean has(Node node,
Predicate<Node> pred,
Predicate<Node> traverseChildrenPred) {
if (pred.apply(node)) {
return true;
}
if (!traverseChildrenPred.apply(node)) {
return false;
}
for (Node c = node.getFirstChild(); c != null; c = c.getNext()) {
if (has(c, pred, traverseChildrenPred)) {
return true;
}
}
return false;
}
/**
* @return The number of times the the predicate is true for the node
* or any of its children.
*/
static int getCount(
Node n, Predicate<Node> pred, Predicate<Node> traverseChildrenPred) {
int total = 0;
if (pred.apply(n)) {
total++;
}
if (traverseChildrenPred.apply(n)) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
total += getCount(c, pred, traverseChildrenPred);
}
}
return total;
}
/**
* Interface for use with the visit method.
* @see #visit
*/
static interface Visitor {
void visit(Node node);
}
/**
* A pre-order traversal, calling Visitor.visit for each child matching
* the predicate.
*/
static void visitPreOrder(Node node,
Visitor visitor,
Predicate<Node> traverseChildrenPred) {
visitor.visit(node);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
if (traverseChildrenPred.apply(node)) {
for (Node c = node.getFirstChild(); c != null; c = c.getNext()) {
visitPreOrder(c, visitor, traverseChildrenPred);
}
}
}
/**
* A post-order traversal, calling Visitor.visit for each child matching
* the predicate.
*/
static void visitPostOrder(Node node,
Visitor visitor,
Predicate<Node> traverseChildrenPred) {
if (traverseChildrenPred.apply(node)) {
for (Node c = node.getFirstChild(); c != null; c = c.getNext()) {
visitPostOrder(c, visitor, traverseChildrenPred);
}
}
visitor.visit(node);
}
/**
* @return Whether a TRY node has a finally block.
*/
static boolean hasFinally(Node n) {
Preconditions.checkArgument(n.isTry());
return n.getChildCount() == 3;
}
/**
* @return The BLOCK node containing the CATCH node (if any)
* of a TRY.
*/
static Node getCatchBlock(Node n) {
Preconditions.checkArgument(n.isTry());
return n.getFirstChild().getNext();
}
/**
* @return Whether BLOCK (from a TRY node) contains a CATCH.
* @see NodeUtil#getCatchBlock
*/
static boolean hasCatchHandler(Node n) {
Preconditions.checkArgument(n.isBlock());
return n.hasChildren() && n.getFirstChild().isCatch();
}
/**
* @param fnNode The function.
* @return The Node containing the Function parameters.
*/
public static Node getFunctionParameters(Node fnNode) {
// Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ]
Preconditions.checkArgument(fnNode.isFunction());
return fnNode.getFirstChild().getNext();
}
/**
* <p>Determines whether a variable is constant:
* <ol>
* <li>In Normalize, any name that matches the
* {@link CodingConvention#isConstant(String)} is annotated with an
* IS_CONSTANT_NAME property.
* </ol>
*
* @param node A NAME or STRING node
* @return True if a name node represents a constant variable
*/
static boolean isConstantName(Node node) {
return node.getBooleanProp(Node.IS_CONSTANT_NAME);
}
/** Whether the given name is constant by coding convention. */
static boolean isConstantByConvention(
CodingConvention convention, Node node, Node parent) {
if (parent.isGetProp() && node == parent.getLastChild()) {
return convention.isConstantKey(node.getString());
} else if (isObjectLitKey(node)) {
return convention.isConstantKey(node.getString());
} else if (node.isName()) {
return convention.isConstant(node.getString());
}
return false;
}
/**
* Get the JSDocInfo for a function.
*/
public static JSDocInfo getFunctionJSDocInfo(Node n) {
Preconditions.checkState(n.isFunction());
JSDocInfo fnInfo = n.getJSDocInfo();
if (fnInfo == null
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> && NodeUtil.isFunctionExpression(n)) {
// Look for the info on other nodes.
Node parent = n.getParent();
if (parent.isAssign()) {
// on ASSIGNs
fnInfo = parent.getJSDocInfo();
} else if (parent.isName()) {
// on var NAME = function() { ... };
fnInfo = parent.getParent().getJSDocInfo();
}
}
return fnInfo;
}
/**
* @param n The node.
* @return The source name property on the node or its ancestors.
*/
public static String getSourceName(Node n) {
String sourceName = null;
while (sourceName == null && n != null) {
sourceName = n.getSourceFileName();
n = n.getParent();
}
return sourceName;
}
/**
* @param n The node.
* @return The source name property on the node or its ancestors.
*/
public static StaticSourceFile getSourceFile(Node n) {
StaticSourceFile sourceName = null;
while (sourceName == null && n != null) {
sourceName = n.getStaticSourceFile();
n = n.getParent();
}
return sourceName;
}
/**
* @param n The node.
* @return The InputId property on the node or its ancestors.
*/
public static InputId getInputId(Node n) {
while (n != null && !n.isScript()) {
n = n.getParent();
}
return (n != null && n.isScript()) ? n.getInputId() : null;
}
/**
* A new CALL node with the "FREE_CALL" set based on call target.
*/
static Node newCallNode(Node callTarget, Node... parameters) {
boolean isFreeCall = !isGet(callTarget);
Node call = IR.call(callTarget);
call.putBooleanProp(Node.FREE_CALL, isFreeCall);
for (Node parameter : parameters) {
call.addChildToBack(parameter);
}
return call;
}
/**
* @return Whether the node is known to be a value that is not referenced
* elsewhere.
*/
static boolean evaluatesToLocalValue(Node value) {
return evaluatesToLocalValue(value, Predicates.<Node>alwaysFalse());
}
/**
* @param locals A predicate to apply to unknown local values.
* @return Whether the node is known to be a value that is not a reference
* outside the expression scope.
*/
static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) {
switch (value.getType()) {
case Token.CAST:
return evaluatesToLocalValue(value.getFirstChild(), locals);
case Token.ASSIGN:
// A result that is aliased by a non-local name, is the effectively the
// same as returning a non-local name, but this doesn't matter if the
// value is immutable.
return NodeUtil.isImmutableValue(value.getLastChild())
|| (locals.apply(value)
&& evaluatesToLocalValue(value.getLastChild(), locals));
case Token.COMMA:
return evaluatesToLocalValue(value.getLastChild(), locals);
case Token.AND:
case Token.OR:
return evaluates
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>ToLocalValue(value.getFirstChild(), locals)
&& evaluatesToLocalValue(value.getLastChild(), locals);
case Token.HOOK:
return evaluatesToLocalValue(value.getFirstChild().getNext(), locals)
&& evaluatesToLocalValue(value.getLastChild(), locals);
case Token.INC:
case Token.DEC:
if (value.getBooleanProp(Node.INCRDECR_PROP)) {
return evaluatesToLocalValue(value.getFirstChild(), locals);
} else {
return true;
}
case Token.THIS:
return locals.apply(value);
case Token.NAME:
return isImmutableValue(value) || locals.apply(value);
case Token.GETELEM:
case Token.GETPROP:
// There is no information about the locality of object properties.
return locals.apply(value);
case Token.CALL:
return callHasLocalResult(value)
|| isToStringMethodCall(value)
|| locals.apply(value);
case Token.NEW:
return newHasLocalResult(value)
|| locals.apply(value);
case Token.FUNCTION:
case Token.REGEXP:
case Token.ARRAYLIT:
case Token.OBJECTLIT:
// Literals objects with non-literal children are allowed.
return true;
case Token.DELPROP:
case Token.IN:
// TODO(johnlenz): should IN operator be included in #isSimpleOperator?
return true;
default:
// Other op force a local value:
// x = '' + g (x is now an local string)
// x -= g (x is now an local number)
if (isAssignmentOp(value)
|| isSimpleOperator(value)
|| isImmutableValue(value)) {
return true;
}
throw new IllegalStateException(
"Unexpected expression node" + value +
"\n parent:" + value.getParent());
}
}
/**
* Given the first sibling, this returns the nth
* sibling or null if no such sibling exists.
* This is like "getChildAtIndex" but returns null for non-existent indexes.
*/
private static Node getNthSibling(Node first, int index) {
Node sibling = first;
while (index != 0 && sibling != null) {
sibling = sibling.getNext();
index--;
}
return sibling;
}
/**
* Given the function, this returns the nth
* argument or null if no such parameter exists.
*/
static Node getArgumentForFunction(Node function, int index) {
Preconditions.checkState(function.isFunction());
return getNthSibling(
function.getFirstChild().getNext().getFirstChild(), index);
}
/**
* Given the new or call, this returns the nth
* argument of the call or null if no such argument exists.
*/
static Node getArgumentForCallOrNew(Node call, int index) {
Preconditions.checkState(isCallOrNew(call));
return getNthSibling(
call.getFirstChild().getNext(), index);
}
/**
* Returns whether this is a target of a call or new.
*/
static boolean isCallOrNewTarget(Node target) {
Node parent = target.getParent();
return parent != null
&& NodeUtil.isCallOrNew(parent)
&& parent.getFirstChild()
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Nodes is false, we are comparing the initial AST to the
// final AST. Don't check unmarked nodes b/c they may have been changed by
// non-loopable passes.
// If verifyUnchangedNodes is true, we are comparing the ASTs before & after
// a pass. Check all scope roots.
final Map<Node, Node> mtoc = map;
final boolean checkUnchanged = verifyUnchangedNodes;
Node clone = mtoc.get(main);
if (main.getChangeTime() > clone.getChangeTime()) {
Preconditions.checkState(!main.isEquivalentToShallow(clone));
} else if (checkUnchanged) {
Preconditions.checkState(main.isEquivalentToShallow(clone));
}
visitPreOrder(main,
new Visitor() {
@Override
public void visit(Node n) {
if (n.isFunction() && mtoc.containsKey(n)) {
Node clone = mtoc.get(n);
if (n.getChangeTime() > clone.getChangeTime()) {
Preconditions.checkState(!n.isEquivalentToShallow(clone));
} else if (checkUnchanged) {
Preconditions.checkState(n.isEquivalentToShallow(clone));
}
}
}
},
Predicates.<Node>alwaysTrue());
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.EnumType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.ObjectType;
import javax.annotation.Nullable;
/**
* Set the JSDocInfo on all types.
*
* Propagates JSDoc across the type graph, but not across the symbol graph.
* This means that if you have:
* <code>
* var x = new Foo();
* x.bar;
* </code>
* then the JSType attached to x.bar may get associated JSDoc, but the
* Node and Var will not.
*
* JSDoc is initially attached to AST Nodes at parse time.
* There are 3 ways that JSDoc get propagated across the type system.
* 1) Nominal types (e.g., constructors) may contain JSDocInfo for their
* declaration.
* 2) Object types have a JSDocInfo slot for each property on that type.
* 3) Shape types (like structural functions) may have JSDocInfo.
*
* #1 and #2 should be self-explanatory, and non-controversial. #3 is
* a bit trickier. It means that if you have:
* <code>
* /** @param {number} x /
* Foo.prototype.bar = goog.abstractMethod;
* </code>
* the JSDocInfo will appear in two places in the type system: in the 'bar'
* slot of Foo.prototype, and on the function expression type created by
* this expression.
*
* @author nicksantos@google.com (Nick Santos)
*/
class InferJSDocInfo extends AbstractPostOrderCallback
implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
@SuppressWarnings("unused")
private boolean inExterns;
InferJSDocInfo(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
if (externs != null) {
inExterns = true;
Node
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Traversal.traverse(compiler, externs, this);
}
if (root != null) {
inExterns = false;
NodeTraversal.traverse(compiler, root, this);
}
}
@Override
public void hotSwapScript(Node root, Node originalRoot) {
Preconditions.checkNotNull(root);
Preconditions.checkState(root.isScript());
inExterns = false;
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
JSDocInfo docInfo;
switch (n.getType()) {
// Infer JSDocInfo on types of all type declarations on variables.
case Token.NAME:
if (parent == null) {
return;
}
// Only allow JSDoc on VARs, function declarations, and assigns.
if (!parent.isVar() &&
!NodeUtil.isFunctionDeclaration(parent) &&
!(parent.isAssign() &&
n == parent.getFirstChild())) {
return;
}
// There are four places the doc info could live.
// 1) A FUNCTION node.
// /** ... */ function f() { ... }
// 2) An ASSIGN parent.
// /** ... */ x = function () { ... }
// 3) A NAME parent.
// var x, /** ... */ y = function() { ... }
// 4) A VAR gramps.
// /** ... */ var x = function() { ... }
docInfo = n.getJSDocInfo();
if (docInfo == null &&
!(parent.isVar() &&
!parent.hasOneChild())) {
docInfo = parent.getJSDocInfo();
}
// Try to find the type of the NAME.
JSType varType = n.getJSType();
if (varType == null && parent.isFunction()) {
varType = parent.getJSType();
}
// If we have no type to attach JSDocInfo to, then there's nothing
// we can do.
if (varType == null || docInfo == null) {
return;
}
// Dereference the type. If the result is not an object, or already
// has docs attached, then do nothing.
ObjectType objType = dereferenceToObject(varType);
if (objType == null || objType.getJSDocInfo() != null) {
return;
}
attachJSDocInfoToNominalTypeOrShape(objType, docInfo, n.getString());
break;
case Token.GETPROP:
// Infer JSDocInfo on properties.
// There are two ways to write doc comments on a property.
//
// 1)
// /** @deprecated */
// obj.prop = ...
//
// 2)
// /** @deprecated */
// obj.prop;
if (parent.isExprResult() ||
(parent.isAssign() &&
parent.getFirstChild() == n)) {
docInfo = n.getJSDocInfo();
if (docInfo == null) {
docInfo = parent.getJSDocInfo();
}
if (docInfo != null) {
ObjectType lhsType =
dereferenceToObject(n.getFirstChild().getJS
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> of all the properties of this record type.
* @param declared Whether this is a declared or synthesized type.
* A synthesized record type is just used for bookkeeping
* in the type system. A declared record type was actually used in the
* user's program.
* @throws IllegalStateException if the {@code RecordProperty} associated
* with a property is null.
*/
RecordType(JSTypeRegistry registry, Map<String, RecordProperty> properties,
boolean declared) {
super(registry, null, null);
setPrettyPrint(true);
this.declared = declared;
for (String property : properties.keySet()) {
RecordProperty prop = properties.get(property);
if (prop == null) {
throw new IllegalStateException(
"RecordProperty associated with a property should not be null!");
}
if (declared) {
defineDeclaredProperty(
property, prop.getType(), prop.getPropertyNode());
} else {
defineSynthesizedProperty(
property, prop.getType(), prop.getPropertyNode());
}
}
// Freeze the record type.
isFrozen = true;
}
/** @return Is this synthesized for internal bookkeeping? */
boolean isSynthetic() {
return !declared;
}
boolean checkRecordEquivalenceHelper(
RecordType otherRecord, EquivalenceMethod eqMethod) {
Set<String> keySet = getOwnPropertyNames();
Set<String> otherKeySet = otherRecord.getOwnPropertyNames();
if (!otherKeySet.equals(keySet)) {
return false;
}
for (String key : keySet) {
if (!otherRecord.getPropertyType(key).checkEquivalenceHelper(
getPropertyType(key), eqMethod)) {
return false;
}
}
return true;
}
@Override
public ObjectType getImplicitPrototype() {
return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, Node propertyNode) {
if (isFrozen) {
return false;
}
return super.defineProperty(propertyName, type, inferred,
propertyNode);
}
JSType getGreatestSubtypeHelper(JSType that) {
if (that.isRecordType()) {
RecordType thatRecord = that.toMaybeRecordType();
RecordTypeBuilder builder = new RecordTypeBuilder(registry);
builder.setSynthesized(true);
// The greatest subtype consists of those *unique* properties of both
// record types. If any property conflicts, then the NO_TYPE type
// is returned.
for (String property : getOwnPropertyNames()) {
if (thatRecord.hasProperty(property) &&
!thatRecord.getPropertyType(property).isInvariant(
getPropertyType(property))) {
return registry.getNativeObjectType(JSTypeNative.NO_TYPE);
}
builder.addProperty(property, getPropertyType(property),
getPropertyNode(property));
}
for (String property : thatRecord.getOwnPropertyNames()) {
if (!hasProperty(property)) {
builder.addProperty(property, thatRecord.getPropertyType(property),
thatRecord.getPropertyNode(property));
}
}
return builder.build();
}
JSType greatestSubtype = registry.getNativeType(
JSTypeNative.NO
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>_OBJECT_TYPE);
JSType thatRestrictedToObj =
registry.getNativeType(JSTypeNative.OBJECT_TYPE)
.getGreatestSubtype(that);
if (!thatRestrictedToObj.isEmptyType()) {
// In this branch, the other type is some object type. We find
// the greatest subtype with the following algorithm:
// 1) For each property "x" of this record type, take the union
// of all classes with a property "x" with a compatible property type.
// and which are a subtype of {@code that}.
// 2) Take the intersection of all of these unions.
for (String propName : getOwnPropertyNames()) {
JSType propType = getPropertyType(propName);
UnionTypeBuilder builder = new UnionTypeBuilder(registry);
for (ObjectType alt :
registry.getEachReferenceTypeWithProperty(propName)) {
JSType altPropType = alt.getPropertyType(propName);
if (altPropType != null && !alt.isEquivalentTo(this) &&
alt.isSubtype(that) &&
propType.isInvariant(altPropType)) {
builder.addAlternate(alt);
}
}
greatestSubtype = greatestSubtype.getLeastSupertype(builder.build());
}
}
return greatestSubtype;
}
@Override
RecordType toMaybeRecordType() {
return this;
}
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtypeHelper(this, that)) {
return true;
}
// Top of the record types is the empty record, or OBJECT_TYPE.
if (registry.getNativeObjectType(
JSTypeNative.OBJECT_TYPE).isSubtype(that)) {
return true;
}
// A type is a subtype of a record type if it itself is a record
// type and it has at least the same members as the parent record type
// with the same types.
if (!that.isRecordType()) {
return false;
}
return RecordType.isSubtype(this, that.toMaybeRecordType());
}
/** Determines if typeA is a subtype of typeB */
static boolean isSubtype(ObjectType typeA, RecordType typeB) {
// typeA is a subtype of record type typeB iff:
// 1) typeA has all the properties declared in typeB.
// 2) And for each property of typeB,
// 2a) if the property of typeA is declared, it must be equal
// to the type of the property of typeB,
// 2b) otherwise, it must be a subtype of the property of typeB.
//
// To figure out why this is true, consider the following pseudo-code:
// /** @type {{a: (Object,null)}} */ var x;
// /** @type {{a: !Object}} */ var y;
// var z = {a: {}};
// x.a = null;
//
// y cannot be assigned to x, because line 4 would violate y's declared
// properties. But z can be assigned to x. Even though z and y are the
// same type, the properties of z are inferred--and so an assignment
// to the property of z would not violate any restrictions
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2004 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.jstype.StaticReference;
import com.google.javascript.rhino.jstype.StaticScope;
import com.google.javascript.rhino.jstype.StaticSlot;
import com.google.javascript.rhino.jstype.StaticSourceFile;
import com.google.javascript.rhino.jstype.StaticSymbolTable;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Scope contains information about a variable scope in JavaScript.
* Scopes can be nested, a scope points back to its parent scope.
* A Scope contains information about variables defined in that scope.
* <p>
* A Scope is also used as a lattice element for flow-sensitive type inference.
* As a lattice element, a Scope is viewed as a map from names to types. A name
* not in the map is considered to have the bottom type. The join of two maps m1
* and m2 is the map of the union of names with {@link JSType#getLeastSupertype}
* to meet the m1 type and m2 type.
*
* @see NodeTraversal
* @see DataFlowAnalysis
*
*/
public class Scope
implements StaticScope<JSType>, StaticSymbolTable<Scope.Var, Scope.Var> {
private final Map<String, Var> vars = new LinkedHashMap<String, Var>();
private final Scope parent;
private final int depth;
private final Node rootNode;
/** Whether this is a bottom scope for the purposes of type inference. */
private final boolean isBottom;
private Var arguments;
private static final Predicate<Var> DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES =
new Predicate<Var>() {
@Override public boolean apply(Var var) {
return var.getParentNode() != null &&
var.getType() == null && // no declared type
var.getParentNode().is
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Var() &&
!var.isExtern();
}
};
/** Stores info about a variable */
public static class Var
implements StaticSlot<JSType>, StaticReference<JSType> {
/** name */
final String name;
/** Var node */
final Node nameNode;
/**
* The variable's type.
*/
private JSType type;
/**
* Whether the variable's type has been inferred or is declared. An inferred
* type may change over time (as more code is discovered), whereas a
* declared type is a static contract that must be matched.
*/
private final boolean typeInferred;
/** Input source */
final CompilerInput input;
/**
* The index at which the var is declared. e..g if it's 0, it's the first
* declared variable in that scope
*/
final int index;
/** The enclosing scope */
final Scope scope;
/** @see #isMarkedEscaped */
private boolean markedEscaped = false;
/** @see #isMarkedAssignedExactlyOnce */
private boolean markedAssignedExactlyOnce = false;
/**
* Creates a variable.
*
* @param inferred whether its type is inferred (as opposed to declared)
*/
private Var(boolean inferred, String name, Node nameNode, JSType type,
Scope scope, int index, CompilerInput input) {
this.name = name;
this.nameNode = nameNode;
this.type = type;
this.scope = scope;
this.index = index;
this.input = input;
this.typeInferred = inferred;
}
/**
* Gets the name of the variable.
*/
@Override
public String getName() {
return name;
}
/**
* Gets the node for the name of the variable.
*/
@Override
public Node getNode() {
return nameNode;
}
CompilerInput getInput() {
return input;
}
@Override
public StaticSourceFile getSourceFile() {
return nameNode.getStaticSourceFile();
}
@Override
public Var getSymbol() {
return this;
}
@Override
public Var getDeclaration() {
return nameNode == null ? null : this;
}
/**
* Gets the parent of the name node.
*/
public Node getParentNode() {
return nameNode == null ? null : nameNode.getParent();
}
/**
* Whether this is a bleeding function (an anonymous named function
* that bleeds into the inner scope).
*/
public boolean isBleedingFunction() {
return NodeUtil.isFunctionExpression(getParentNode());
}
/**
* Gets the scope where this variable is declared.
*/
Scope getScope() {
return scope;
}
/**
* Returns whether this is a global variable.
*/
public boolean isGlobal() {
return scope.isGlobal();
}
/**
* Returns whether this is a local variable.
*/
public boolean isLocal() {
return scope.isLocal();
}
/**
* Returns whether this is defined in an extern file.
*/
boolean isExtern() {
return input == null || input.isExtern();
}
/**
* Returns {@code true} if the variable is declared as a constant,
* based on the value reported by {@
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>code NodeUtil}.
*/
public boolean isConst() {
return nameNode != null && NodeUtil.isConstantName(nameNode);
}
/**
* Returns {@code true} if the variable is declared as a define.
* A variable is a define if it is annotated by {@code @define}.
*/
public boolean isDefine() {
JSDocInfo info = getJSDocInfo();
return info != null && info.isDefine();
}
public Node getInitialValue() {
return NodeUtil.getRValueOfLValue(nameNode);
}
/**
* Gets this variable's type. To know whether this type has been inferred,
* see {@code #isTypeInferred()}.
*/
@Override
public JSType getType() {
return type;
}
/**
* Returns the name node that produced this variable.
*/
public Node getNameNode() {
return nameNode;
}
/**
* Gets the JSDocInfo for the variable.
*/
@Override
public JSDocInfo getJSDocInfo() {
return nameNode == null ? null : NodeUtil.getBestJSDocInfo(nameNode);
}
/**
* Sets this variable's type.
* @throws IllegalStateException if the variable's type is not inferred
*/
void setType(JSType type) {
Preconditions.checkState(isTypeInferred());
this.type = type;
}
/**
* Resolve this variable's type.
*/
void resolveType(ErrorReporter errorReporter) {
if (type != null) {
type = type.resolve(errorReporter, scope);
}
}
/**
* Returns whether this variable's type is inferred. To get the variable's
* type, see {@link #getType()}.
*/
@Override
public boolean isTypeInferred() {
return typeInferred;
}
public String getInputName() {
if (input == null) {
return "<non-file>";
}
return input.getName();
}
public boolean isNoShadow() {
JSDocInfo info = getJSDocInfo();
return info != null && info.isNoShadow();
}
@Override public boolean equals(Object other) {
if (!(other instanceof Var)) {
return false;
}
Var otherVar = (Var) other;
return otherVar.nameNode == nameNode;
}
@Override public int hashCode() {
return nameNode.hashCode();
}
@Override
public String toString() {
return "Scope.Var " + name + "{" + type + "}";
}
/**
* Record that this is escaped by an inner scope.
*
* In other words, it's assigned in an inner scope so that it's much harder
* to make assertions about its value at a given point.
*/
void markEscaped() {
markedEscaped = true;
}
/**
* Whether this is escaped by an inner scope.
* Notice that not all scope creators record this information.
*/
boolean isMarkedEscaped() {
return markedEscaped;
}
/**
* Record that this is assigned exactly once..
*
* In other words, it's assigned in an inner scope so that it's much harder
* to make assertions about its value at a given point.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> */
void markAssignedExactlyOnce() {
markedAssignedExactlyOnce = true;
}
/**
* Whether this is assigned exactly once.
* Notice that not all scope creators record this information.
*/
boolean isMarkedAssignedExactlyOnce() {
return markedAssignedExactlyOnce;
}
}
/**
* A special subclass of Var used to distinguish "arguments" in the current
* scope.
*/
// TODO(johnlenz): Include this the list of Vars for the scope.
public static class Arguments extends Var {
Arguments(Scope scope) {
super(
false, // no inferred
"arguments", // always arguments
null, // no declaration node
// TODO(johnlenz): provide the type of "Arguments".
null, // no type info
scope,
-1, // no variable index
null // input
);
}
@Override public boolean equals(Object other) {
if (!(other instanceof Arguments)) {
return false;
}
Arguments otherVar = (Arguments) other;
return otherVar.scope.getRootNode() == scope.getRootNode();
}
@Override public int hashCode() {
return System.identityHashCode(this);
}
}
/**
* Creates a Scope given the parent Scope and the root node of the scope.
* @param parent The parent Scope. Cannot be null.
* @param rootNode Typically the FUNCTION node.
*/
Scope(Scope parent, Node rootNode) {
Preconditions.checkNotNull(parent);
Preconditions.checkArgument(rootNode != parent.rootNode);
this.parent = parent;
this.rootNode = rootNode;
this.isBottom = false;
this.depth = parent.depth + 1;
}
/**
* Creates a empty Scope (bottom of the lattice).
* @param rootNode Typically a FUNCTION node or the global BLOCK node.
* @param isBottom Whether this is the bottom of a lattice. Otherwise,
* it must be a global scope.
*/
private Scope(Node rootNode, boolean isBottom) {
this.parent = null;
this.rootNode = rootNode;
this.isBottom = isBottom;
this.depth = 0;
}
static Scope createGlobalScope(Node rootNode) {
return new Scope(rootNode, false);
}
static Scope createLatticeBottom(Node rootNode) {
return new Scope(rootNode, true);
}
/** The depth of the scope. The global scope has depth 0. */
int getDepth() {
return depth;
}
/** Whether this is the bottom of the lattice. */
boolean isBottom() {
return isBottom;
}
/**
* Gets the container node of the scope. This is typically the FUNCTION
* node or the global BLOCK/SCRIPT node.
*/
@Override
public Node getRootNode() {
return rootNode;
}
public Scope getParent() {
return parent;
}
Scope getGlobalScope() {
Scope result = this;
while (result.getParent() != null) {
result = result.getParent();
}
return result;
}
@Override
public StaticScope<JSType> getParentScope() {
return parent;
}
/**
* Gets the type of {@code this} in the current scope
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>.
*/
@Override
public JSType getTypeOfThis() {
if (isGlobal()) {
return ObjectType.cast(rootNode.getJSType());
}
Preconditions.checkState(rootNode.isFunction());
JSType nodeType = rootNode.getJSType();
if (nodeType != null && nodeType.isFunctionType()) {
return nodeType.toMaybeFunctionType().getTypeOfThis();
} else {
return parent.getTypeOfThis();
}
}
/**
* Declares a variable whose type is inferred.
*
* @param name name of the variable
* @param nameNode the NAME node declaring the variable
* @param type the variable's type
* @param input the input in which this variable is defined.
*/
Var declare(String name, Node nameNode, JSType type, CompilerInput input) {
return declare(name, nameNode, type, input, true);
}
/**
* Declares a variable.
*
* @param name name of the variable
* @param nameNode the NAME node declaring the variable
* @param type the variable's type
* @param input the input in which this variable is defined.
* @param inferred Whether this variable's type is inferred (as opposed
* to declared).
*/
Var declare(String name, Node nameNode,
JSType type, CompilerInput input, boolean inferred) {
Preconditions.checkState(name != null && name.length() > 0);
// Make sure that it's declared only once
Preconditions.checkState(vars.get(name) == null);
Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input);
vars.put(name, var);
return var;
}
/**
* Undeclares a variable, to be used when the compiler optimizes out
* a variable and removes it from the scope.
*/
void undeclare(Var var) {
Preconditions.checkState(var.scope == this);
Preconditions.checkState(vars.get(var.name) == var);
vars.remove(var.name);
}
@Override
public Var getSlot(String name) {
return getVar(name);
}
@Override
public Var getOwnSlot(String name) {
return vars.get(name);
}
/**
* Returns the variable, may be null
*/
public Var getVar(String name) {
Var var = vars.get(name);
if (var != null) {
return var;
} else if (parent != null) { // Recurse up the parent Scope
return parent.getVar(name);
} else {
return null;
}
}
/**
* Get a unique VAR object to represents "arguments" within this scope
*/
public Var getArgumentsVar() {
if (arguments == null) {
arguments = new Arguments(this);
}
return arguments;
}
/**
* Returns true if a variable is declared.
*/
public boolean isDeclared(String name, boolean recurse) {
Scope scope = this;
if (scope.vars.containsKey(name)) {
return true;
}
if (scope.parent != null && recurse) {
return scope.parent.isDeclared(name, recurse);
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
return false;
}
/**
* Return an iterator over all of the variables declared in this scope.
*/
public Iterator<Var> getVars() {
return vars.values().iterator();
}
/**
* Return an iterable over all of the variables declared in this scope.
*/
Iterable<Var> getVarIterable() {
return vars.values();
}
@Override
public Iterable<Var> getReferences(Var var) {
return ImmutableList.of(var);
}
@Override
public StaticScope<JSType> getScope(Var var) {
return var.scope;
}
@Override
public Iterable<Var> getAllSymbols() {
return Collections.unmodifiableCollection(vars.values());
}
/**
* Returns number of variables in this scope
*/
public int getVarCount() {
return vars.size();
}
/**
* Returns whether this is the global scope.
*/
public boolean isGlobal() {
return parent == null;
}
/**
* Returns whether this is a local scope (i.e. not the global scope).
*/
public boolean isLocal() {
return parent != null;
}
/**
* Gets all variables declared with "var" but without declared types attached.
*/
public Iterator<Var> getDeclarativelyUnboundVarsWithoutTypes() {
return Iterators.filter(
getVars(), DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES);
}
static interface TypeResolver {
void resolveTypes();
}
private TypeResolver typeResolver;
/** Resolve all type references. Only used on typed scopes. */
void resolveTypes() {
if (typeResolver != null) {
typeResolver.resolveTypes();
typeResolver = null;
}
}
void setTypeResolver(TypeResolver resolver) {
this.typeResolver = resolver;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
import com.google.javascript.rhino.ErrorReporter;
/**
* All type, representing all values.
*/
public final class AllType extends JSType {
private static final long serialVersionUID = 1L;
AllType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isAllType() {
return true;
}
@Override
public boolean matchesStringContext() {
// Be lenient.
return true;
}
@Override
public boolean matchesObjectContext() {
// Be lenient.
return true;
}
@Override
public boolean canBeCalled() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
return UNKNOWN;
}
@Override
String toStringHelper(boolean forAnnotations) {
return "*";
}
@Override
public String getDisplayName() {
return "<Any Type>";
}
@Override
public boolean hasDisplayName() {
return true;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseAllType();
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseAllType(that);
}
@Override
public BooleanLiteral
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Set getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
return this;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* John Lenz
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino;
import java.io.Serializable;
/**
* An id used uniquely identify a CompilerInput
* @author johnlenz@google.com (John Lenz)
*/
public class InputId implements Serializable {
public static final long serialVersionUID = 1L;
private final String id;
public InputId(String id) {
this.id = id;
}
public String getIdName() {
return id;
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
return id.equals(((InputId) obj).id);
}
@Override
public String toString() {
return "InputId: " + getIdName();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> this.validNodeTypeA = validNodeTypeA;
this.validNodeTypeB = validNodeTypeB;
this.registerFunction = registerFunction;
}
boolean isValidNodeType(int type) {
return type == validNodeTypeA || type == validNodeTypeB;
}
boolean isCorrectRegisterFunction(TweakFunction registerFunction) {
Preconditions.checkNotNull(registerFunction);
return this.registerFunction == registerFunction;
}
boolean isGetterFunction() {
return registerFunction != null;
}
String getName() {
return name;
}
String getExpectedTypeName() {
return expectedTypeName;
}
Node createDefaultValueNode() {
switch (this) {
case REGISTER_BOOLEAN:
return IR.falseNode();
case REGISTER_NUMBER:
return IR.number(0);
case REGISTER_STRING:
return IR.string("");
default:
throw new IllegalStateException();
}
}
}
// A map of function name -> TweakFunction.
private static final Map<String, TweakFunction> TWEAK_FUNCTIONS_MAP;
static {
TWEAK_FUNCTIONS_MAP = Maps.newHashMap();
for (TweakFunction func : TweakFunction.values()) {
TWEAK_FUNCTIONS_MAP.put(func.getName(), func);
}
}
ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks,
Map<String, Node> compilerDefaultValueOverrides) {
this.compiler = compiler;
this.stripTweaks = stripTweaks;
// Having the map sorted is required for the unit tests to be deterministic.
this.compilerDefaultValueOverrides = Maps.newTreeMap();
this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides);
}
@Override
public void process(Node externs, Node root) {
CollectTweaksResult result = collectTweaks(root);
applyCompilerDefaultValueOverrides(result.tweakInfos);
boolean changed = false;
if (stripTweaks) {
changed = stripAllCalls(result.tweakInfos);
} else if (!compilerDefaultValueOverrides.isEmpty()) {
changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls);
}
if (changed) {
compiler.reportCodeChange();
}
}
/**
* Passes the compiler default value overrides to the JS by replacing calls
* to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value;
*/
private boolean replaceGetCompilerOverridesCalls(
List<TweakFunctionCall> calls) {
for (TweakFunctionCall call : calls) {
Node callNode = call.callNode;
Node objNode = createCompilerDefaultValueOverridesVarNode(callNode);
callNode.getParent().replaceChild(callNode, objNode);
}
return !calls.isEmpty();
}
/**
* Removes all CALL nodes in the given TweakInfos, replacing calls to getter
* functions with the tweak's default value.
*/
private boolean stripAllCalls(Map<String, TweakInfo> tweakInfos) {
for (TweakInfo tweakInfo : tweakInfos.values()) {
boolean isRegistered = tweakInfo.isRegistered();
for (TweakFunctionCall functionCall : tweakInfo.functionCalls) {
Node callNode = functionCall.callNode;
Node parent
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>weakInfo> tweakInfos;
final List<TweakFunctionCall> getOverridesCalls;
CollectTweaksResult(Map<String, TweakInfo> tweakInfos,
List<TweakFunctionCall> getOverridesCalls) {
this.tweakInfos = tweakInfos;
this.getOverridesCalls = getOverridesCalls;
}
}
/**
* Processes all calls to goog.tweak functions.
*/
private final class CollectTweaks extends AbstractPostOrderCallback {
final Map<String, TweakInfo> allTweaks = Maps.newHashMap();
final List<TweakFunctionCall> getOverridesCalls = Lists.newArrayList();
@SuppressWarnings("incomplete-switch")
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (!n.isCall()) {
return;
}
String callName = n.getFirstChild().getQualifiedName();
TweakFunction tweakFunc = TWEAK_FUNCTIONS_MAP.get(callName);
if (tweakFunc == null) {
return;
}
if (tweakFunc == TweakFunction.GET_COMPILER_OVERRIDES) {
getOverridesCalls.add(
new TweakFunctionCall(t.getSourceName(), tweakFunc, n));
return;
}
// Ensure the first parameter (the tweak ID) is a string literal.
Node tweakIdNode = n.getFirstChild().getNext();
if (!tweakIdNode.isString()) {
compiler.report(t.makeError(tweakIdNode, NON_LITERAL_TWEAK_ID_ERROR));
return;
}
String tweakId = tweakIdNode.getString();
// Make sure there is a TweakInfo structure for it.
TweakInfo tweakInfo = allTweaks.get(tweakId);
if (tweakInfo == null) {
tweakInfo = new TweakInfo(tweakId);
allTweaks.put(tweakId, tweakInfo);
}
switch (tweakFunc) {
case REGISTER_BOOLEAN:
case REGISTER_NUMBER:
case REGISTER_STRING:
// Ensure the ID contains only valid characters.
if (!ID_MATCHER.matchesAllOf(tweakId)) {
compiler.report(t.makeError(tweakIdNode, INVALID_TWEAK_ID_ERROR));
}
// Ensure tweaks are registered in the global scope.
if (!t.inGlobalScope()) {
compiler.report(
t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId));
break;
}
// Ensure tweaks are registered only once.
if (tweakInfo.isRegistered()) {
compiler.report(
t.makeError(n, TWEAK_MULTIPLY_REGISTERED_ERROR, tweakId));
break;
}
Node tweakDefaultValueNode = tweakIdNode.getNext().getNext();
tweakInfo.addRegisterCall(t.getSourceName(), tweakFunc, n,
tweakDefaultValueNode);
break;
case OVERRIDE_DEFAULT_VALUE:
// Ensure tweaks overrides occur in the global scope.
if (!t.inGlobalScope()) {
compiler.report(
t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId));
break;
}
// Ensure tweak overrides occur before the
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
/**
* The type of individual elements of an enum type
* (see {@link EnumType}).
*/
public class EnumElementType extends ObjectType {
private static final long serialVersionUID = 1L;
/**
* The primitive type this enum element type wraps. For instance, in
* the following code defining the {@code LOCAL_CODES} enum
* <pre>var LOCAL_CODES = {A: 3, B: 9, C: 8}</pre>
* the primitive type of the the constants is {@code number}.
*/
private JSType primitiveType;
// The primitive type, if it is an object.
private ObjectType primitiveObjectType;
private final String name;
EnumElementType(JSTypeRegistry registry, JSType elementType,
String name) {
super(registry);
this.primitiveType = elementType;
this.primitiveObjectType = elementType.toObjectType();
this.name = name;
}
@Override public PropertyMap getPropertyMap() {
return primitiveObjectType == null
? PropertyMap.immutableEmptyMap()
: primitiveObjectType.getPropertyMap();
}
@Override
public EnumElementType toMaybeEnumElementType() {
return this;
}
@Override
public boolean matchesNumberContext() {
return primitiveType.matchesNumberContext();
}
@Override
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> public boolean matchesStringContext() {
return primitiveType.matchesStringContext();
}
@Override
public boolean matchesObjectContext() {
return primitiveType.matchesObjectContext();
}
@Override
public boolean canBeCalled() {
return primitiveType.canBeCalled();
}
@Override
public boolean isObject() {
return primitiveType.isObject();
}
@Override
public TernaryValue testForEquality(JSType that) {
return primitiveType.testForEquality(that);
}
/**
* This predicate determines whether objects of this type can have the null
* value, and therefore can appear in contexts where null is expected.
*
* @return true for everything but Number and Boolean types.
*/
@Override
public boolean isNullable() {
return primitiveType.isNullable();
}
@Override
public boolean isNominalType() {
return hasReferenceName();
}
/**
* If this is equal to a NamedType object, its hashCode must be equal
* to the hashCode of the NamedType object.
*/
@Override
public int hashCode() {
if (hasReferenceName()) {
return getReferenceName().hashCode();
} else {
return super.hashCode();
}
}
@Override
String toStringHelper(boolean forAnnotations) {
return forAnnotations ?
primitiveType.toString() :
(getReferenceName() + ".<" + primitiveType + ">");
}
@Override
public String getReferenceName() {
return name;
}
@Override
public boolean hasReferenceName() {
return true;
}
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtypeHelper(this, that)) {
return true;
} else {
return primitiveType.isSubtype(that);
}
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseEnumElementType(this);
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseEnumElementType(this, that);
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, Node propertyNode) {
// nothing
return true;
}
@Override
public ObjectType getImplicitPrototype() {
return null;
}
@Override
public JSType findPropertyType(String propertyName) {
return primitiveType.findPropertyType(propertyName);
}
@Override
public FunctionType getConstructor() {
return primitiveObjectType == null ?
null : primitiveObjectType.getConstructor();
}
@Override
public JSType autoboxesTo() {
return primitiveType.autoboxesTo();
}
/**
* Gets the primitive type of this enum element.
*/
public JSType getPrimitiveType() {
return primitiveType;
}
/**
* Returns the infimum of a enum element type and another type, or null
* if the infimum is empty.
*
* This can be a little bit weird. For example, suppose you have an enum
* of {(string|number)}, and you want the greatest subtype of the enum
* and a {number}.
*
* The infimum is non-empty. But at the same time, we don't really have
* a name for this infimum. It's equivalent to
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> "elements of this enum that
* are numbers".
*
* The best we can do is make up a new type. This is similar to what
* we do in UnionType#meet, which kind-of-sort-of makes sense, because
* an EnumElementType is a union of instances of a type.
*/
JSType meet(JSType that) {
JSType meetPrimitive = primitiveType.getGreatestSubtype(that);
if (meetPrimitive.isEmptyType()) {
return null;
} else {
return new EnumElementType(registry, meetPrimitive, name);
}
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
primitiveType = primitiveType.resolve(t, scope);
primitiveObjectType = ObjectType.cast(primitiveType);
return this;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* A simple {@link ErrorReporter} that collects warnings and errors and makes
* them accessible via {@link #errors()} and {@link #warnings()}.
*
*/
public class SimpleErrorReporter implements ErrorReporter {
private List<String> warnings = null;
private List<String> errors = null;
@Override
public void warning(String message, String sourceName, int line,
int lineOffset) {
if (warnings == null) {
warnings = new ArrayList<String>();
}
warnings.add(formatDetailedMessage(message, sourceName, line));
}
@Override
public void error(String message, String sourceName, int line,
int lineOffset) {
if (errors == null) {
errors = new ArrayList<String>();
}
errors.add(formatDetailedMessage(message, sourceName, line));
}
/**
* Returns the list of errors, or {@code null} if there were none.
*/
public List<String> errors() {
return errors;
}
/**
* Returns the list of warnings, or {@code null} if there were none.
*/
public List<String> warnings() {
return warnings;
}
private String formatDetailedMessage(
String
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>SC_TEMPLATE_TYPE_DUPLICATED",
"Only one parameter type must be the template type");
static final DiagnosticType TEMPLATE_TYPE_EXPECTED = DiagnosticType.warning(
"JSC_TEMPLATE_TYPE_EXPECTED",
"The template type must be a parameter type");
static final DiagnosticType THIS_TYPE_NON_OBJECT =
DiagnosticType.warning(
"JSC_THIS_TYPE_NON_OBJECT",
"@this type of a function must be an object\n" +
"Actual type: {0}");
static final DiagnosticType SAME_INTERFACE_MULTIPLE_IMPLEMENTS =
DiagnosticType.warning(
"JSC_SAME_INTERFACE_MULTIPLE_IMPLEMENTS",
"Cannot @implement the same interface more than once\n" +
"Repeated interface: {0}");
private class ExtendedTypeValidator implements Predicate<JSType> {
@Override
public boolean apply(JSType type) {
ObjectType objectType = ObjectType.cast(type);
if (objectType == null) {
reportWarning(EXTENDS_NON_OBJECT, formatFnName(), type.toString());
return false;
} else if (objectType.isEmptyType()) {
reportWarning(RESOLVED_TAG_EMPTY, "@extends", formatFnName());
return false;
} else if (objectType.isUnknownType()) {
if (hasMoreTagsToResolve(objectType)) {
return true;
} else {
reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName);
return false;
}
} else {
return true;
}
}
}
private class ImplementedTypeValidator implements Predicate<JSType> {
@Override
public boolean apply(JSType type) {
ObjectType objectType = ObjectType.cast(type);
if (objectType == null) {
reportError(BAD_IMPLEMENTED_TYPE, fnName);
return false;
} else if (objectType.isEmptyType()) {
reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName);
return false;
} else if (objectType.isUnknownType()) {
if (hasMoreTagsToResolve(objectType)) {
return true;
} else {
reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName);
return false;
}
} else {
return true;
}
}
}
/**
* @param fnName The function name.
* @param compiler The compiler.
* @param errorRoot The node to associate with any warning generated by
* this builder.
* @param sourceName A source name for associating any warnings that
* we have to emit.
* @param scope The syntactic scope.
*/
FunctionTypeBuilder(String fnName, AbstractCompiler compiler,
Node errorRoot, String sourceName, Scope scope) {
Preconditions.checkNotNull(errorRoot);
this.fnName = fnName == null ? "" : fnName;
this.codingConvention = compiler.getCodingConvention();
this.typeRegistry = compiler.getTypeRegistry();
this.errorRoot = errorRoot;
this.sourceName = sourceName;
this.compiler = compiler;
this.scope = scope;
}
/** Format the function name for use in warnings. */
String formatFnName() {
return fnName.isEmpty() ? "<anonymous>" : fn
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Expr =
fromInlineDoc ? info.getType() : info.getReturnType();
if (returnTypeExpr != null) {
returnType = returnTypeExpr.evaluate(scope, typeRegistry);
returnTypeInferred = false;
}
}
return this;
}
/**
* Infer the role of the function (whether it's a constructor or interface)
* and what it inherits from in JSDocInfo.
*/
FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) {
if (info != null) {
isConstructor = info.isConstructor();
makesStructs = info.makesStructs();
makesDicts = info.makesDicts();
isInterface = info.isInterface();
if (makesStructs && !isConstructor) {
reportWarning(CONSTRUCTOR_REQUIRED, "@struct", formatFnName());
} else if (makesDicts && !isConstructor) {
reportWarning(CONSTRUCTOR_REQUIRED, "@dict", formatFnName());
}
// Class template types, which can be used in the scope of a constructor
// definition.
ImmutableList<String> typeParameters = info.getTemplateTypeNames();
if (!typeParameters.isEmpty()) {
if (isConstructor || isInterface) {
ImmutableList.Builder<TemplateType> builder = ImmutableList.builder();
for (String typeParameter : typeParameters) {
builder.add(typeRegistry.createTemplateType(typeParameter));
}
classTemplateTypeNames = builder.build();
typeRegistry.setTemplateTypeNames(classTemplateTypeNames);
}
}
// base type
if (info.hasBaseType()) {
if (isConstructor) {
JSType maybeBaseType =
info.getBaseType().evaluate(scope, typeRegistry);
if (maybeBaseType != null &&
maybeBaseType.setValidator(new ExtendedTypeValidator())) {
baseType = (ObjectType) maybeBaseType;
}
} else {
reportWarning(EXTENDS_WITHOUT_TYPEDEF, formatFnName());
}
}
// Implemented interfaces (for constructors only).
if (info.getImplementedInterfaceCount() > 0) {
if (isConstructor) {
implementedInterfaces = Lists.newArrayList();
Set<JSType> baseInterfaces = new HashSet<JSType>();
for (JSTypeExpression t : info.getImplementedInterfaces()) {
JSType maybeInterType = t.evaluate(scope, typeRegistry);
if (maybeInterType != null &&
maybeInterType.setValidator(new ImplementedTypeValidator())) {
// Disallow implementing the same base (not templatized) interface
// type more than once.
JSType baseInterface = maybeInterType;
if (baseInterface.toMaybeTemplatizedType() != null) {
baseInterface =
baseInterface.toMaybeTemplatizedType().getReferencedType();
}
if (baseInterfaces.contains(baseInterface)) {
reportWarning(SAME_INTERFACE_MULTIPLE_IMPLEMENTS,
baseInterface.toString());
} else {
baseInterfaces.add(baseInterface);
}
implementedInterfaces.add((ObjectType) maybeInterType);
}
}
} else if (isInterface) {
reportWarning(
TypeCheck.CONFLICTING_IMPLEMENTED_TYPE, formatFnName());
} else {
reportWarning(CONSTRUCTOR_REQUIRED, "@implements", formatFnName
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> = null;
boolean isOptionalParam = isOptionalParameter(arg, info);
isVarArgs = isVarArgsParameter(arg, info);
if (info != null && info.hasParameterType(argumentName)) {
parameterType =
info.getParameterType(argumentName).evaluate(scope, typeRegistry);
} else if (arg.getJSDocInfo() != null && arg.getJSDocInfo().hasType()) {
parameterType =
arg.getJSDocInfo().getType().evaluate(scope, typeRegistry);
} else if (oldParameterType != null &&
oldParameterType.getJSType() != null) {
parameterType = oldParameterType.getJSType();
isOptionalParam = oldParameterType.isOptionalArg();
isVarArgs = oldParameterType.isVarArgs();
} else {
parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE);
}
warnedAboutArgList |= addParameter(
builder, parameterType, warnedAboutArgList,
isOptionalParam,
isVarArgs);
if (oldParameterType != null) {
oldParameterType = oldParameterType.getNext();
}
}
// Copy over any old parameters that aren't in the param list.
if (!isVarArgs) {
while (oldParameterType != null && !isVarArgs) {
builder.newParameterFromNode(oldParameterType);
oldParameterType = oldParameterType.getNext();
}
}
for (String inexistentName : allJsDocParams) {
reportWarning(INEXISTANT_PARAM, inexistentName, formatFnName());
}
parametersNode = builder.build();
return this;
}
/**
* @return Whether the given param is an optional param.
*/
private boolean isOptionalParameter(
Node param, @Nullable JSDocInfo info) {
if (codingConvention.isOptionalParameter(param)) {
return true;
}
String paramName = param.getString();
return info != null && info.hasParameterType(paramName) &&
info.getParameterType(paramName).isOptionalArg();
}
/**
* Determine whether this is a var args parameter.
* @return Whether the given param is a var args param.
*/
private boolean isVarArgsParameter(
Node param, @Nullable JSDocInfo info) {
if (codingConvention.isVarArgsParameter(param)) {
return true;
}
String paramName = param.getString();
return info != null && info.hasParameterType(paramName) &&
info.getParameterType(paramName).isVarArgs();
}
/**
* Infer the template type from the doc info.
*/
FunctionTypeBuilder inferTemplateTypeName(
@Nullable JSDocInfo info, JSType ownerType) {
// NOTE: these template type names may override a list
// of inherited ones from an overridden function.
if (info != null && !info.getTemplateTypeNames().isEmpty()) {
ImmutableList.Builder<TemplateType> builder = ImmutableList.builder();
for (String key : info.getTemplateTypeNames()) {
builder.add(typeRegistry.createTemplateType(key));
}
templateTypeNames = builder.build();
}
ImmutableList<TemplateType> keys = templateTypeNames;
if (ownerType != null) {
ImmutableList<TemplateType> ownerTypeKeys =
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Names();
/** Gets a list of variables whose properties are escaped. */
Set<String> getEscapedQualifiedNames();
/** Gets the number of times each variable has been assigned. */
Multiset<String> getAssignedNameCounts();
}
static class UnknownFunctionContents implements FunctionContents {
private static UnknownFunctionContents singleton =
new UnknownFunctionContents();
static FunctionContents get() {
return singleton;
}
@Override
public Node getSourceNode() {
return null;
}
@Override
public boolean mayBeFromExterns() {
return true;
}
@Override
public boolean mayHaveNonEmptyReturns() {
return true;
}
@Override
public boolean mayHaveSingleThrow() {
return true;
}
@Override
public Iterable<String> getEscapedVarNames() {
return ImmutableList.of();
}
@Override
public Set<String> getEscapedQualifiedNames() {
return ImmutableSet.of();
}
@Override
public Multiset<String> getAssignedNameCounts() {
return ImmutableMultiset.of();
}
}
static class AstFunctionContents implements FunctionContents {
private final Node n;
private boolean hasNonEmptyReturns = false;
private Set<String> escapedVarNames;
private Set<String> escapedQualifiedNames;
private final Multiset<String> assignedVarNames = HashMultiset.create();
AstFunctionContents(Node n) {
this.n = n;
}
@Override
public Node getSourceNode() {
return n;
}
@Override
public boolean mayBeFromExterns() {
return n.isFromExterns();
}
@Override
public boolean mayHaveNonEmptyReturns() {
return hasNonEmptyReturns;
}
void recordNonEmptyReturn() {
hasNonEmptyReturns = true;
}
@Override
public boolean mayHaveSingleThrow() {
Node block = n.getLastChild();
return block.hasOneChild() && block.getFirstChild().isThrow();
}
@Override
public Iterable<String> getEscapedVarNames() {
return escapedVarNames == null
? ImmutableList.<String>of() : escapedVarNames;
}
void recordEscapedVarName(String name) {
if (escapedVarNames == null) {
escapedVarNames = Sets.newHashSet();
}
escapedVarNames.add(name);
}
@Override
public Set<String> getEscapedQualifiedNames() {
return escapedQualifiedNames == null
? ImmutableSet.<String>of() : escapedQualifiedNames;
}
void recordEscapedQualifiedName(String name) {
if (escapedQualifiedNames == null) {
escapedQualifiedNames = Sets.newHashSet();
}
escapedQualifiedNames.add(name);
}
@Override
public Multiset<String> getAssignedNameCounts() {
return assignedVarNames;
}
void recordAssignedName(String name) {
assignedVarNames.add(name);
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import com.google.javascript.jscomp.GlobalNamespace.Name;
import com.google.javascript.jscomp.GlobalNamespace.Ref;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import java.util.Set;
/**
* Checks references to undefined properties of global variables.
*
* @author nicksantos@google.com (Nick Santos)
*/
class CheckGlobalNames implements CompilerPass {
private final AbstractCompiler compiler;
private final CodingConvention convention;
private final CheckLevel level;
private GlobalNamespace namespace = null;
private final Set<String> objectPrototypeProps = Sets.newHashSet();
private final Set<String> functionPrototypeProps = Sets.newHashSet();
// Warnings
static final DiagnosticType UNDEFINED_NAME_WARNING = DiagnosticType.warning(
"JSC_UNDEFINED_NAME",
"{0} is never defined");
static final DiagnosticType NAME_DEFINED_LATE_WARNING =
DiagnosticType.warning(
"JSC_NAME_DEFINED_LATE",
"{0} defined before its owner. {1} is defined at {2}:{3}");
static final DiagnosticType STRICT_MODULE_DEP_QNAME =
DiagnosticType.disabled(
"JSC_STRICT_MODULE_DEP_QNAME",
"module {0} cannot reference {2}, defined in " +
"module {1}");
/**
* Creates a pass to check global name references at the given warning level.
*/
CheckGlobalNames(AbstractCompiler compiler, CheckLevel level) {
this.compiler = compiler;
this.convention = compiler.getCodingConvention();
this.level = level;
}
/**
* Injects a pre-computed global namespace, so that the same namespace
* can be re-used for multiple check passes. Returns this for easy chaining.
*/
CheckGlobalNames injectNamespace(GlobalNamespace namespace) {
Preconditions.checkArgument(namespace.hasExternsRoot());
this.namespace = namespace;
return this;
}
@Override
public void process(Node externs, Node root) {
if (namespace == null) {
namespace = new GlobalNamespace(compiler, externs, root);
}
// Find prototype properties that will affect our analysis.
Preconditions.checkState(namespace.hasExternsRoot());
findPrototypeProps("Object", objectPrototypeProps);
findPrototypeProps("Function", functionPrototypeProps);
objectPrototypeProps.addAll(
convention.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>() &&
(convention.getClassesDefinedByCall(aliaser) != null ||
convention.getSingletonGetterClassName(aliaser) != null);
if (!isKnownAlias) {
parentIsAliased = true;
}
}
}
}
if (parentIsAliased) {
return false;
}
if (objectPrototypeProps.contains(name.getBaseName())) {
return false;
}
if (name.parent.type == Name.Type.OBJECTLIT) {
return true;
}
if (name.parent.type == Name.Type.FUNCTION &&
name.parent.isDeclaredType() &&
!functionPrototypeProps.contains(name.getBaseName())) {
return true;
}
return false;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ObjectType;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* This describes the Closure-specific JavaScript coding conventions.
*
*/
public class ClosureCodingConvention extends CodingConventions.Proxy {
private static final long serialVersionUID = 1L;
static final DiagnosticType OBJECTLIT_EXPECTED = DiagnosticType.warning(
"JSC_REFLECT_OBJECTLIT_EXPECTED",
"Object literal expected as second argument");
private final Set<String> indirectlyDeclaredProperties;
public ClosureCodingConvention() {
this(CodingConventions.getDefault());
}
public ClosureCodingConvention(CodingConvention wrapped) {
super(wrapped);
Set<String> props = Sets.newHashSet(
"superClass_",
"instance_",
"getInstance");
props.addAll(wrapped.getIndirectlyDeclaredProperties());
indirectlyDeclaredProperties = ImmutableSet.copyOf(props);
}
/**
* Closure's goog.inherits adds a {@code superClass_} property to the
* subclass, and a {@code constructor} property.
*/
@Override
public void applySubclassRelationship(FunctionType parentCtor,
FunctionType childCtor, SubclassType type) {
super.applySubclassRelationship(parentCtor, childCtor, type);
if (type == SubclassType.INHERITS) {
childCtor.defineDeclaredProperty("superClass_",
parentCtor.getPrototype(), childCtor.getSource());
childCtor.getPrototype().defineDeclaredProperty("constructor",
// Notice that constructor functions do not need to be covariant
// on the superclass.
// So if G extends F, new G() and new F() can accept completely
// different argument types, but G.prototype.constructor needs
// to be covariant on F.prototype.constructor.
// To get around this, we just turn off type-checking on arguments
// and return types of
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> G.prototype.constructor.
childCtor.cloneWithoutArrowType(),
childCtor.getSource());
}
}
/**
* {@inheritDoc}
*
* <p>Understands several different inheritance patterns that occur in
* Google code (various uses of {@code inherits} and {@code mixin}).
*/
@Override
public SubclassRelationship getClassesDefinedByCall(Node callNode) {
SubclassRelationship relationship =
super.getClassesDefinedByCall(callNode);
if (relationship != null) {
return relationship;
}
Node callName = callNode.getFirstChild();
SubclassType type = typeofClassDefiningName(callName);
if (type != null) {
Node subclass = null;
Node superclass = callNode.getLastChild();
// There are six possible syntaxes for a class-defining method:
// SubClass.inherits(SuperClass)
// goog.inherits(SubClass, SuperClass)
// goog$inherits(SubClass, SuperClass)
// SubClass.mixin(SuperClass.prototype)
// goog.mixin(SubClass.prototype, SuperClass.prototype)
// goog$mixin(SubClass.prototype, SuperClass.prototype)
boolean isDeprecatedCall = callNode.getChildCount() == 2 &&
callName.isGetProp();
if (isDeprecatedCall) {
// SubClass.inherits(SuperClass)
subclass = callName.getFirstChild();
} else if (callNode.getChildCount() == 3) {
// goog.inherits(SubClass, SuperClass)
subclass = callName.getNext();
} else {
return null;
}
if (type == SubclassType.MIXIN) {
// Only consider mixins that mix two prototypes as related to
// inheritance.
if (!endsWithPrototype(superclass)) {
return null;
}
if (!isDeprecatedCall) {
if (!endsWithPrototype(subclass)) {
return null;
}
// Strip off the prototype from the name.
subclass = subclass.getFirstChild();
}
superclass = superclass.getFirstChild();
}
// bail out if either of the side of the "inherits"
// isn't a real class name. This prevents us from
// doing something weird in cases like:
// goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2)
if (subclass != null &&
subclass.isUnscopedQualifiedName() &&
superclass.isUnscopedQualifiedName()) {
return new SubclassRelationship(type, subclass, superclass);
}
}
return null;
}
/**
* Determines whether the given node is a class-defining name, like
* "inherits" or "mixin."
* @return The type of class-defining name, or null.
*/
private SubclassType typeofClassDefiningName(Node callName) {
// Check if the method name matches one of the class-defining methods.
String methodName = null;
if (callName.isGetProp()) {
methodName = callName.getLastChild().getString();
} else if (callName.isName()) {
String name = callName.getString();
int dollarIndex = name.lastIndexOf('$');
if (dollarIndex != -1) {
methodName = name.substring(dollarIndex + 1);
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> }
if (methodName != null) {
if (methodName.equals("inherits")) {
return SubclassType.INHERITS;
} else if (methodName.equals("mixin")) {
return SubclassType.MIXIN;
}
}
return null;
}
@Override
public boolean isSuperClassReference(String propertyName) {
return "superClass_".equals(propertyName) ||
super.isSuperClassReference(propertyName);
}
/**
* Given a qualified name node, returns whether "prototype" is at the end.
* For example:
* a.b.c => false
* a.b.c.prototype => true
*/
private boolean endsWithPrototype(Node qualifiedName) {
return qualifiedName.isGetProp() &&
qualifiedName.getLastChild().getString().equals("prototype");
}
/**
* Extracts X from goog.provide('X'), if the applied Node is goog.
*
* @return The extracted class name, or null.
*/
@Override
public String extractClassNameIfProvide(Node node, Node parent){
return extractClassNameIfGoog(node, parent, "goog.provide");
}
/**
* Extracts X from goog.require('X'), if the applied Node is goog.
*
* @return The extracted class name, or null.
*/
@Override
public String extractClassNameIfRequire(Node node, Node parent){
return extractClassNameIfGoog(node, parent, "goog.require");
}
private static String extractClassNameIfGoog(Node node, Node parent,
String functionName){
String className = null;
if (NodeUtil.isExprCall(parent)) {
Node callee = node.getFirstChild();
if (callee != null && callee.isGetProp()) {
String qualifiedName = callee.getQualifiedName();
if (functionName.equals(qualifiedName)) {
Node target = callee.getNext();
if (target != null && target.isString()) {
className = target.getString();
}
}
}
}
return className;
}
/**
* Use closure's implementation.
* @return closure's function name for exporting properties.
*/
@Override
public String getExportPropertyFunction() {
return "goog.exportProperty";
}
/**
* Use closure's implementation.
* @return closure's function name for exporting symbols.
*/
@Override
public String getExportSymbolFunction() {
return "goog.exportSymbol";
}
@Override
public List<String> identifyTypeDeclarationCall(Node n) {
Node callName = n.getFirstChild();
if ("goog.addDependency".equals(callName.getQualifiedName()) &&
n.getChildCount() >= 3) {
Node typeArray = callName.getNext().getNext();
if (typeArray.isArrayLit()) {
List<String> typeNames = Lists.newArrayList();
for (Node name = typeArray.getFirstChild(); name != null;
name = name.getNext()) {
if (name.isString()) {
typeNames.add(name.getString());
}
}
return typeNames;
}
}
return super.identifyTypeDeclarationCall(n);
}
@Override
public String getAbstractMethodName() {
return "goog.abstractMethod";
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
@Override
public String getSingletonGetterClassName(Node callNode) {
Node callArg = callNode.getFirstChild();
String callName = callArg.getQualifiedName();
// Use both the original name and the post-CollapseProperties name.
if (!("goog.addSingletonGetter".equals(callName) ||
"goog$addSingletonGetter".equals(callName)) ||
callNode.getChildCount() != 2) {
return super.getSingletonGetterClassName(callNode);
}
return callArg.getNext().getQualifiedName();
}
@Override
public void applySingletonGetter(FunctionType functionType,
FunctionType getterType, ObjectType objectType) {
super.applySingletonGetter(functionType, getterType, objectType);
functionType.defineDeclaredProperty("getInstance", getterType,
functionType.getSource());
functionType.defineDeclaredProperty("instance_", objectType,
functionType.getSource());
}
@Override
public String getGlobalObject() {
return "goog.global";
}
private final Set<String> propertyTestFunctions = ImmutableSet.of(
"goog.isDef", "goog.isNull", "goog.isDefAndNotNull",
"goog.isString", "goog.isNumber", "goog.isBoolean",
"goog.isFunction", "goog.isArray", "goog.isObject");
@Override
public boolean isPropertyTestFunction(Node call) {
Preconditions.checkArgument(call.isCall());
return propertyTestFunctions.contains(
call.getFirstChild().getQualifiedName()) ||
super.isPropertyTestFunction(call);
}
@Override
public ObjectLiteralCast getObjectLiteralCast(Node callNode) {
Preconditions.checkArgument(callNode.isCall());
ObjectLiteralCast proxyCast = super.getObjectLiteralCast(callNode);
if (proxyCast != null) {
return proxyCast;
}
Node callName = callNode.getFirstChild();
if (!"goog.reflect.object".equals(callName.getQualifiedName()) ||
callNode.getChildCount() != 3) {
return null;
}
Node typeNode = callName.getNext();
if (!typeNode.isQualifiedName()) {
return null;
}
Node objectNode = typeNode.getNext();
if (!objectNode.isObjectLit()) {
return new ObjectLiteralCast(null, null, OBJECTLIT_EXPECTED);
}
return new ObjectLiteralCast(
typeNode.getQualifiedName(), typeNode.getNext(), null);
}
@Override
public boolean isOptionalParameter(Node parameter) {
return false;
}
@Override
public boolean isVarArgsParameter(Node parameter) {
return false;
}
@Override
public boolean isPrivate(String name) {
return false;
}
@Override
public Collection<AssertionFunctionSpec> getAssertionFunctions() {
return ImmutableList.<AssertionFunctionSpec>of(
new AssertionFunctionSpec("goog.asserts.assert"),
new AssertionFunctionSpec("goog.asserts.assertNumber",
JSTypeNative.NUMBER_TYPE),
new AssertionFunctionSpec("goog.asserts.assertString",
JSTypeNative.STRING_TYPE),
new AssertionFunctionSpec("goog.asserts.assertFunction",
JSTypeNative.FUNCTION_INSTANCE_TYPE),
new AssertionFunctionSpec("goog.asserts.assertObject",
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> JSTypeNative.OBJECT_TYPE),
new AssertionFunctionSpec("goog.asserts.assertArray",
JSTypeNative.ARRAY_TYPE),
new AssertFunctionByTypeName("goog.asserts.assertElement", "Element"),
new AssertInstanceofSpec("goog.asserts.assertInstanceof")
);
}
@Override
public Bind describeFunctionBind(Node n, boolean useTypeInfo) {
Bind result = super.describeFunctionBind(n, useTypeInfo);
if (result != null) {
return result;
}
if (!n.isCall()) {
return null;
}
Node callTarget = n.getFirstChild();
String name = callTarget.getQualifiedName();
if (name != null) {
if (name.equals("goog.bind")
|| name.equals("goog$bind")) {
// goog.bind(fn, self, args...);
Node fn = callTarget.getNext();
if (fn == null) {
return null;
}
Node thisValue = safeNext(fn);
Node parameters = safeNext(thisValue);
return new Bind(fn, thisValue, parameters);
}
if (name.equals("goog.partial") || name.equals("goog$partial")) {
// goog.partial(fn, args...);
Node fn = callTarget.getNext();
if (fn == null) {
return null;
}
Node thisValue = null;
Node parameters = safeNext(fn);
return new Bind(fn, thisValue, parameters);
}
}
return null;
}
@Override
public Collection<String> getIndirectlyDeclaredProperties() {
return indirectlyDeclaredProperties;
}
private Node safeNext(Node n) {
if (n != null) {
return n.getNext();
}
return null;
}
/**
* A function that will throw an exception when if the value is not
* an instanceof a specific type.
*/
public static class AssertInstanceofSpec extends AssertionFunctionSpec {
public AssertInstanceofSpec(String functionName) {
super(functionName, JSTypeNative.OBJECT_TYPE);
}
/**
* Returns the type for a type assertion, or null if the function asserts
* that the node must not be null or undefined.
*/
@Override
public JSType getAssertedType(Node call, JSTypeRegistry registry) {
if (call.getChildCount() > 2) {
Node constructor = call.getFirstChild().getNext().getNext();
if (constructor != null) {
JSType ownerType = constructor.getJSType();
if (ownerType != null
&& ownerType.isFunctionType()
&& ownerType.isConstructor()) {
FunctionType functionType = ((FunctionType) ownerType);
return functionType.getInstanceType();
}
}
}
return super.getAssertedType(call, registry);
}
}
/**
* A function that will throw an exception when the value is not an
* instanceof the given type name, for instance "Element".
*/
public static class AssertFunctionByTypeName extends AssertionFunctionSpec {
private final String typeName;
public AssertFunctionByTypeName(String functionName, String typeName) {
super(functionName);
this.typeName = typeName;
}
/** {@inheritDoc} */
@Override
public
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ModificationVisitor;
import com.google.javascript.rhino.jstype.TemplateType;
import com.google.javascript.rhino.jstype.TemplateTypeMap;
import java.util.ArrayDeque;
/**
* Uses a TemplateTypeMap to replace TemplateTypes with their associated JSType
* values.
*
* @author izaakr@google.com (Izaak Rubin)
*/
public class TemplateTypeMapReplacer extends ModificationVisitor {
private final TemplateTypeMap replacements;
private ArrayDeque<TemplateType> visitedTypes;
public TemplateTypeMapReplacer(
JSTypeRegistry registry, TemplateTypeMap replacements) {
super(registry);
this.replacements = replacements;
this.visitedTypes = new ArrayDeque<TemplateType>();
}
@Override
public JSType caseTemplateType(TemplateType type) {
if (replacements.hasTemplateKey(type)) {
if (hasVisitedType(type) || !replacements.hasTemplateType(type)) {
// If we have already encountered this TemplateType during replacement
// (i.e. there is a reference loop), or there is no JSType substitution
// for the TemplateType, return the
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>: return "is_dispatcher";
case DIRECTIVES: return "directives";
case DIRECT_EVAL: return "direct_eval";
case FREE_CALL: return "free_call";
case STATIC_SOURCE_FILE: return "source_file";
case INPUT_ID: return "input_id";
case LENGTH: return "length";
case SLASH_V: return "slash_v";
case INFERRED_FUNCTION: return "inferred";
case CHANGE_TIME: return "change_time";
case REFLECTED_OBJECT: return "reflected_object";
default:
throw new IllegalStateException("unexpected prop id " + propType);
}
}
private static class NumberNode extends Node {
private static final long serialVersionUID = 1L;
NumberNode(double number) {
super(Token.NUMBER);
this.number = number;
}
public NumberNode(double number, int lineno, int charno) {
super(Token.NUMBER, lineno, charno);
this.number = number;
}
@Override
public double getDouble() {
return this.number;
}
@Override
public void setDouble(double d) {
this.number = d;
}
@Override
boolean isEquivalentTo(
Node node, boolean compareJsType, boolean recur, boolean shallow) {
boolean equiv = super.isEquivalentTo(node, compareJsType, recur, shallow);
if (equiv) {
double thisValue = getDouble();
double thatValue = ((NumberNode) node).getDouble();
if (thisValue == thatValue) {
// detect the difference between 0.0 and -0.0.
return (thisValue != 0.0) || (1 / thisValue == 1 / thatValue);
}
}
return false;
}
private double number;
}
private static class StringNode extends Node {
private static final long serialVersionUID = 1L;
StringNode(int type, String str) {
super(type);
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
StringNode(int type, String str, int lineno, int charno) {
super(type, lineno, charno);
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
/**
* returns the string content.
* @return non null.
*/
@Override
public String getString() {
return this.str;
}
/**
* sets the string content.
* @param str the new value. Non null.
*/
@Override
public void setString(String str) {
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
@Override
boolean isEquivalentTo(
Node node, boolean compareJsType, boolean recur, boolean shallow) {
return (super.isEquivalentTo(node, compareJsType, recur, shallow)
&& this.str.equals(((StringNode) node).str));
}
/**
* If the property is not defined, this was not a quoted key.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> The
* QUOTED_PROP int property is only assigned to STRING tokens used as
* object lit keys.
* @return true if this was a quoted string key in an object literal.
*/
@Override
public boolean isQuotedString() {
return getBooleanProp(QUOTED_PROP);
}
/**
* This should only be called for STRING nodes created in object lits.
*/
@Override
public void setQuotedString() {
putBooleanProp(QUOTED_PROP, true);
}
private String str;
}
// PropListItems must be immutable so that they can be shared.
private interface PropListItem {
int getType();
PropListItem getNext();
PropListItem chain(PropListItem next);
Object getObjectValue();
int getIntValue();
}
private abstract static class AbstractPropListItem
implements PropListItem, Serializable {
private static final long serialVersionUID = 1L;
private final PropListItem next;
private final int propType;
AbstractPropListItem(int propType, PropListItem next) {
this.propType = propType;
this.next = next;
}
@Override
public int getType() {
return propType;
}
@Override
public PropListItem getNext() {
return next;
}
@Override
public abstract PropListItem chain(PropListItem next);
}
// A base class for Object storing props
private static class ObjectPropListItem
extends AbstractPropListItem {
private static final long serialVersionUID = 1L;
private final Object objectValue;
ObjectPropListItem(int propType, Object objectValue, PropListItem next) {
super(propType, next);
this.objectValue = objectValue;
}
@Override
public int getIntValue() {
throw new UnsupportedOperationException();
}
@Override
public Object getObjectValue() {
return objectValue;
}
@Override
public String toString() {
return objectValue == null ? "null" : objectValue.toString();
}
@Override
public PropListItem chain(PropListItem next) {
return new ObjectPropListItem(getType(), objectValue, next);
}
}
// A base class for int storing props
private static class IntPropListItem extends AbstractPropListItem {
private static final long serialVersionUID = 1L;
final int intValue;
IntPropListItem(int propType, int intValue, PropListItem next) {
super(propType, next);
this.intValue = intValue;
}
@Override
public int getIntValue() {
return intValue;
}
@Override
public Object getObjectValue() {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return String.valueOf(intValue);
}
@Override
public PropListItem chain(PropListItem next) {
return new IntPropListItem(getType(), intValue, next);
}
}
public Node(int nodeType) {
type = nodeType;
parent = null;
sourcePosition = -1;
}
public Node(int nodeType, Node child) {
Preconditions.checkArgument(child.parent == null,
"new child has existing parent");
Preconditions.checkArgument(child.next == null,
"new child has existing sibling");
type = nodeType;
parent = null;
first = last = child;
child.next = null;
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>this + " is not a number node");
}
}
/**
* Can only be called when <tt>getType() == Token.NUMBER</tt>
* @param value value to set.
*/
public void setDouble(double value) throws UnsupportedOperationException {
if (this.getType() == Token.NUMBER) {
throw new IllegalStateException(
"Number node not created with Node.newNumber");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
/** Can only be called when node has String context. */
public String getString() throws UnsupportedOperationException {
if (this.getType() == Token.STRING) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
/**
* Can only be called for a Token.STRING or Token.NAME.
* @param value the value to set.
*/
public void setString(String value) throws UnsupportedOperationException {
if (this.getType() == Token.STRING || this.getType() == Token.NAME) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
@Override
public String toString() {
return toString(true, true, true);
}
public String toString(
boolean printSource,
boolean printAnnotations,
boolean printType) {
StringBuilder sb = new StringBuilder();
toString(sb, printSource, printAnnotations, printType);
return sb.toString();
}
private void toString(
StringBuilder sb,
boolean printSource,
boolean printAnnotations,
boolean printType) {
sb.append(Token.name(type));
if (this instanceof StringNode) {
sb.append(' ');
sb.append(getString());
} else if (type == Token.FUNCTION) {
sb.append(' ');
// In the case of JsDoc trees, the first child is often not a string
// which causes exceptions to be thrown when calling toString or
// toStringTree.
if (first == null || first.getType() != Token.NAME) {
sb.append("<invalid>");
} else {
sb.append(first.getString());
}
} else if (type == Token.NUMBER) {
sb.append(' ');
sb.append(getDouble());
}
if (printSource) {
int lineno = getLineno();
if (lineno != -1) {
sb.append(' ');
sb.append(lineno);
}
}
if (printAnnotations) {
int[] keys = getSortedPropTypes();
for (int i = 0; i < keys.length; i++) {
int type = keys[i];
PropListItem x = lookupProperty(type);
sb.append(" [");
sb.append(propToString(type));
sb.append(": ");
String value;
switch (type) {
default:
value = x.toString();
break;
}
sb.append(value);
sb.append(']');
}
}
if (printType) {
if (jsType != null) {
String jsTypeString = jsType.toString();
if (jsTypeString != null) {
sb
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> (see {@link #mergeLineCharNo(int, int)}).
*/
protected static int extractLineno(int lineCharNo) {
if (lineCharNo == -1) {
return -1;
} else {
return lineCharNo >>> COLUMN_BITS;
}
}
/**
* Extracts the character number and character number from a merged line
* char number (see {@link #mergeLineCharNo(int, int)}).
*/
protected static int extractCharno(int lineCharNo) {
if (lineCharNo == -1) {
return -1;
} else {
return lineCharNo & COLUMN_MASK;
}
}
//==========================================================================
// Iteration
/**
* <p>Return an iterable object that iterates over this node's children.
* The iterator does not support the optional operation
* {@link Iterator#remove()}.</p>
*
* <p>To iterate over a node's children, one can write</p>
* <pre>Node n = ...;
* for (Node child : n.children()) { ...</pre>
*/
public Iterable<Node> children() {
if (first == null) {
return Collections.emptySet();
} else {
return new SiblingNodeIterable(first);
}
}
/**
* <p>Return an iterable object that iterates over this node's siblings.
* The iterator does not support the optional operation
* {@link Iterator#remove()}.</p>
*
* <p>To iterate over a node's siblings, one can write</p>
* <pre>Node n = ...;
* for (Node sibling : n.siblings()) { ...</pre>
*/
public Iterable<Node> siblings() {
return new SiblingNodeIterable(this);
}
/**
* @see Node#siblings()
*/
private static final class SiblingNodeIterable
implements Iterable<Node>, Iterator<Node> {
private final Node start;
private Node current;
private boolean used;
SiblingNodeIterable(Node start) {
this.start = start;
this.current = start;
this.used = false;
}
@Override
public Iterator<Node> iterator() {
if (!used) {
used = true;
return this;
} else {
// We have already used the current object as an iterator;
// we must create a new SiblingNodeIterable based on this
// iterable's start node.
//
// Since the primary use case for Node.children is in for
// loops, this branch is extremely unlikely.
return (new SiblingNodeIterable(start)).iterator();
}
}
@Override
public boolean hasNext() {
return current != null;
}
@Override
public Node next() {
if (current == null) {
throw new NoSuchElementException();
}
try {
return current;
} finally {
current = current.getNext();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
// ==========================================================================
// Accessors
PropListItem getPropListHeadForTesting() {
return propListHead;
}
public Node getParent() {
return parent;
}
/**
* Gets the ancestor node relative to this.
*
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> * @param level 0 = this, 1 = the parent, etc.
*/
public Node getAncestor(int level) {
Preconditions.checkArgument(level >= 0);
Node node = this;
while (node != null && level-- > 0) {
node = node.getParent();
}
return node;
}
/**
* Iterates all of the node's ancestors excluding itself.
*/
public AncestorIterable getAncestors() {
return new AncestorIterable(this.getParent());
}
/**
* Iterator to go up the ancestor tree.
*/
public static class AncestorIterable implements Iterable<Node> {
private Node cur;
/**
* @param cur The node to start.
*/
AncestorIterable(Node cur) {
this.cur = cur;
}
@Override
public Iterator<Node> iterator() {
return new Iterator<Node>() {
@Override
public boolean hasNext() {
return cur != null;
}
@Override
public Node next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Node n = cur;
cur = cur.getParent();
return n;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
/**
* Check for one child more efficiently than by iterating over all the
* children as is done with Node.getChildCount().
*
* @return Whether the node has exactly one child.
*/
public boolean hasOneChild() {
return first != null && first == last;
}
/**
* Check for more than one child more efficiently than by iterating over all
* the children as is done with Node.getChildCount().
*
* @return Whether the node more than one child.
*/
public boolean hasMoreThanOneChild() {
return first != null && first != last;
}
public int getChildCount() {
int c = 0;
for (Node n = first; n != null; n = n.next) {
c++;
}
return c;
}
// Intended for testing and verification only.
public boolean hasChild(Node child) {
for (Node n = first; n != null; n = n.getNext()) {
if (child == n) {
return true;
}
}
return false;
}
/**
* Checks if the subtree under this node is the same as another subtree.
* Returns null if it's equal, or a message describing the differences.
*/
public String checkTreeEquals(Node node2) {
NodeMismatch diff = checkTreeEqualsImpl(node2);
if (diff != null) {
return "Node tree inequality:" +
"\nTree1:\n" + toStringTree() +
"\n\nTree2:\n" + node2.toStringTree() +
"\n\nSubtree1: " + diff.nodeA.toStringTree() +
"\n\nSubtree2: " + diff.nodeB.toStringTree();
}
return null;
}
/**
* Compare this node to node2 recursively and return the first pair of nodes
* that differs doing a preorder depth-first traversal. Package private for
* testing. Returns null if the nodes are equivalent.
*/
Node
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>THIS_UNMODIFIED
| Node.FLAG_NO_THROWS);
}
/**
* Returns true if this node is a function or constructor call that
* has no side effects.
*/
public boolean isNoSideEffectsCall() {
return areBitFlagsSet(getSideEffectFlags(), NO_SIDE_EFFECTS);
}
/**
* Returns true if this node is a function or constructor call that
* returns a primitive or a local object (an object that has no other
* references).
*/
public boolean isLocalResultCall() {
return areBitFlagsSet(getSideEffectFlags(), FLAG_LOCAL_RESULTS);
}
/** Returns true if this is a new/call that may mutate its arguments. */
public boolean mayMutateArguments() {
return !areBitFlagsSet(getSideEffectFlags(), FLAG_ARGUMENTS_UNMODIFIED);
}
/** Returns true if this is a new/call that may mutate global state or throw. */
public boolean mayMutateGlobalStateOrThrow() {
return !areBitFlagsSet(getSideEffectFlags(),
FLAG_GLOBAL_STATE_UNMODIFIED | FLAG_NO_THROWS);
}
/**
* returns true if all the flags are set in value.
*/
private boolean areBitFlagsSet(int value, int flags) {
return (value & flags) == flags;
}
/**
* This should only be called for STRING nodes children of OBJECTLIT.
*/
public boolean isQuotedString() {
return false;
}
/**
* This should only be called for STRING nodes children of OBJECTLIT.
*/
public void setQuotedString() {
throw new IllegalStateException("not a StringNode");
}
static class NodeMismatch {
final Node nodeA;
final Node nodeB;
NodeMismatch(Node nodeA, Node nodeB) {
this.nodeA = nodeA;
this.nodeB = nodeB;
}
@Override
public boolean equals(Object object) {
if (object instanceof NodeMismatch) {
NodeMismatch that = (NodeMismatch) object;
return that.nodeA.equals(this.nodeA) && that.nodeB.equals(this.nodeB);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(nodeA, nodeB);
}
}
/*** AST type check methods ***/
public boolean isAdd() {
return this.getType() == Token.ADD;
}
public boolean isAnd() {
return this.getType() == Token.AND;
}
public boolean isArrayLit() {
return this.getType() == Token.ARRAYLIT;
}
public boolean isAssign() {
return this.getType() == Token.ASSIGN;
}
public boolean isAssignAdd() {
return this.getType() == Token.ASSIGN_ADD;
}
public boolean isBlock() {
return this.getType() == Token.BLOCK;
}
public boolean isBreak() {
return this.getType() == Token.BREAK;
}
public boolean isCall() {
return this.getType() == Token.CALL;
}
public boolean isCase() {
return this.getType() == Token.CASE;
}
public boolean isCast() {
return this.getType() == Token.CAST;
}
public
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* Number type.
*/
public class NumberType extends ValueType {
private static final long serialVersionUID = 1L;
NumberType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNullable() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isUnknownType() || that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
}
return FALSE;
}
@Override
public boolean isNumberValueType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
// TODO(user): Revisit this for ES4, which is stricter.
return true;
}
@Override
String toStringHelper(boolean forAnnotations) {
return getDisplayName();
}
@Override
public String getDisplayName() {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> return "number";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNumberType();
}
@Override
public JSType autoboxesTo() {
return getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE);
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.common.base.Preconditions;
import com.google.javascript.rhino.Node;
/**
* An object type that is an instance of some function constructor.
*/
class InstanceObjectType extends PrototypeObjectType {
private static final long serialVersionUID = 1L;
private final FunctionType constructor;
InstanceObjectType(JSTypeRegistry registry, FunctionType constructor) {
this(registry, constructor, false);
}
InstanceObjectType(JSTypeRegistry registry, FunctionType constructor,
boolean isNativeType) {
super(registry, null, null, isNativeType, constructor.getTemplateTypeMap());
Preconditions.checkNotNull(constructor);
this.constructor = constructor;
}
@Override
public String getReferenceName() {
return getConstructor().getReferenceName();
}
@Override
public boolean hasReferenceName() {
return getConstructor().hasReferenceName();
}
@Override
public ObjectType getImplicitPrototype() {
return getConstructor().getPrototype();
}
@Override
public FunctionType getConstructor() {
return constructor;
}
@Override
boolean defineProperty(String name, JSType type, boolean inferred,
Node propertyNode) {
ObjectType proto = getImplicitPrototype();
if (proto != null && proto.hasOwnDeclaredProperty(name)) {
return false;
}
return super.defineProperty(name, type, inferred, propertyNode);
}
@Override
String toStringHelper
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>(boolean forAnnotations) {
if (constructor.hasReferenceName()) {
return constructor.getReferenceName();
} else {
return super.toStringHelper(forAnnotations);
}
}
@Override
boolean isTheObjectType() {
return getConstructor().isNativeObjectType()
&& "Object".equals(getReferenceName());
}
@Override
public boolean isInstanceType() {
return true;
}
@Override
public boolean isArrayType() {
return getConstructor().isNativeObjectType()
&& "Array".equals(getReferenceName());
}
@Override
public boolean isStringObjectType() {
return getConstructor().isNativeObjectType()
&& "String".equals(getReferenceName());
}
@Override
public boolean isBooleanObjectType() {
return getConstructor().isNativeObjectType()
&& "Boolean".equals(getReferenceName());
}
@Override
public boolean isNumberObjectType() {
return getConstructor().isNativeObjectType()
&& "Number".equals(getReferenceName());
}
@Override
public boolean isDateType() {
return getConstructor().isNativeObjectType()
&& "Date".equals(getReferenceName());
}
@Override
public boolean isRegexpType() {
return getConstructor().isNativeObjectType()
&& "RegExp".equals(getReferenceName());
}
@Override
public boolean isNominalType() {
return hasReferenceName();
}
/**
* If this is equal to a NamedType object, its hashCode must be equal
* to the hashCode of the NamedType object.
*/
@Override
public int hashCode() {
if (hasReferenceName()) {
return getReferenceName().hashCode();
} else {
return super.hashCode();
}
}
@Override
public Iterable<ObjectType> getCtorImplementedInterfaces() {
return getConstructor().getImplementedInterfaces();
}
@Override
public Iterable<ObjectType> getCtorExtendedInterfaces() {
return getConstructor().getExtendedInterfaces();
}
// The owner will always be a resolved type, so there's no need to set
// the constructor in resolveInternal.
// (it would lead to infinite loops if we did).
// JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope);
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.Map;
/**
* Named groups of DiagnosticTypes exposed by Compiler.
* @author nicksantos@google.com (Nick Santos)
*/
public class DiagnosticGroups {
static final DiagnosticType UNUSED =
DiagnosticType.warning("JSC_UNUSED", "{0}");
public DiagnosticGroups() {}
private static final Map<String, DiagnosticGroup> groupsByName =
Maps.newHashMap();
static DiagnosticGroup registerDeprecatedGroup(String name) {
return registerGroup(name, new DiagnosticGroup(name, UNUSED));
}
static DiagnosticGroup registerGroup(String name,
DiagnosticGroup group) {
groupsByName.put(name, group);
return group;
}
static DiagnosticGroup registerGroup(String name,
DiagnosticType ... types) {
DiagnosticGroup group = new DiagnosticGroup(name, types);
groupsByName.put(name, group);
return group;
}
static DiagnosticGroup registerGroup(String name,
DiagnosticGroup ... groups) {
DiagnosticGroup group = new DiagnosticGroup(name, groups);
groupsByName.put(name, group);
return group;
}
/** Get the registered diagnostic groups, indexed by name. */
protected Map<String, DiagnosticGroup> getRegisteredGroups() {
return ImmutableMap.copyOf(groupsByName);
}
/** Find the diagnostic group registered under the given name. */
public DiagnosticGroup forName(String name) {
return groupsByName.get(name);
}
// A bit of a hack to display the available groups on the command-line.
// New groups should be added to this list if they are public and should
// be listed on the command-line as an available option.
//
// If a group is suppressible on a per-file basis, it should be added
// to parser/ParserConfig.properties
static final String DIAGNOSTIC_GROUP_NAMES =
"accessControls, ambiguousFunctionDecl, checkEventfulObjectDisposal, " +
"checkRegExp, checkStructDictInheritance, checkTypes, checkVars, " +
"const, constantProperty, deprecated, duplicateMessage, es3, " +
"es5Strict, externsValidation, fileoverviewTags, globalThis, " +
"internetExplorerChecks, invalidCasts, misplacedTypeAnnotation, " +
"missingProperties, missingProvide, missingRequire, missingReturn," +
"nonStandardJsDocs, reportUnknownTypes, suspiciousCode
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Maps;
import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.JSDocInfo.Visibility;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.Map;
/**
* Insures '@constructor X' has a 'goog.provide("X")' .
*
*/
class CheckProvides implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
private final CheckLevel checkLevel;
private final CodingConvention codingConvention;
static final DiagnosticType MISSING_PROVIDE_WARNING = DiagnosticType.disabled(
"JSC_MISSING_PROVIDE",
"missing goog.provide(''{0}'')");
CheckProvides(AbstractCompiler compiler, CheckLevel checkLevel) {
this.compiler = compiler;
this.checkLevel = checkLevel;
this.codingConvention = compiler.getCodingConvention();
}
@Override
public void process(Node externs, Node root) {
hotSwapScript(root, null);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
CheckProvidesCallback callback =
new CheckProvidesCallback(codingConvention);
new NodeTraversal(compiler, callback).traverse(scriptRoot);
}
private class CheckProvidesCallback extends AbstractShallowCallback {
private final Map<String, Node> provides = Maps.newHashMap();
private final Map<String, Node> ctors = Maps.newHashMap();
private final CodingConvention convention;
CheckProvidesCallback(CodingConvention convention){
this.convention = convention;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.CALL:
String providedClassName =
codingConvention.extractClassNameIfProvide(n, parent);
if (providedClassName != null) {
provides.put(providedClassName, n);
}
break;
case Token.FUNCTION:
visitFunctionNode(n, parent);
break;
case Token.SCRIPT:
visitScriptNode();
}
}
private void visitFunctionNode(Node n, Node parent) {
Node name = null;
JSDocInfo info = parent.getJSDocInfo();
if (info != null && info.isConstructor()) {
name = parent.getFirstChild();
} else {
// look to the child, maybe it's
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2006 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.javascript.jscomp.Scope.Var;
import com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter;
import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.testing.Asserts;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* Tests {@link TypeCheck}.
*
*/
public class TypeCheckTest extends CompilerTypeTestCase {
private CheckLevel reportMissingOverrides = CheckLevel.WARNING;
private static final String SUGGESTION_CLASS =
"/** @constructor\n */\n" +
"function Suggest() {}\n" +
"Suggest.prototype.a = 1;\n" +
"Suggest.prototype.veryPossible = 1;\n" +
"Suggest.prototype.veryPossible2 = 1;\n";
@Override
public void setUp() throws Exception {
super.setUp();
reportMissingOverrides = CheckLevel.WARNING;
}
public void testInitialTypingScope() {
Scope s = new TypedScopeCreator(compiler,
CodingConventions.getDefault()).createInitialScope(
new Node(Token.BLOCK));
assertTypeEquals(ARRAY_FUNCTION_TYPE, s.getVar("Array").getType());
assertTypeEquals(BOOLEAN_OBJECT_FUNCTION_TYPE,
s.getVar("Boolean").getType());
assertTypeEquals(DATE_FUNCTION_TYPE, s.getVar("Date").getType());
assertTypeEquals(ERROR_FUNCTION_TYPE, s.getVar("Error").getType());
assertTypeEquals(EVAL_ERROR_FUNCTION_TYPE,
s.getVar("EvalError").getType());
assertTypeEquals(NUMBER_OBJECT_FUNCTION_TYPE,
s.getVar("Number").getType());
assertTypeEquals(OBJECT_FUNCTION_TYPE, s.getVar("Object").getType());
assertTypeEquals(RANGE_ERROR_
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.TRUE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* Null type.
*/
public final class NullType extends ValueType {
private static final long serialVersionUID = 1L;
NullType(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNullType() {
return true;
}
@Override
public boolean isNullable() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return false;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public JSType restrictByNotNullOrUndefined() {
return registry.getNativeType(JSTypeNative.NO_TYPE);
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isNullType() || that.isVoidType()) {
return TRUE;
}
if (that.isUnknownType() || that.isNullable()) {
return
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> UNKNOWN;
}
return FALSE;
}
@Override
String toStringHelper(boolean forAnnotations) {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "null";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.FALSE;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNullType();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.javascript.jscomp.ControlFlowGraph.Branch;
import com.google.javascript.jscomp.NodeTraversal.Callback;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.ArrayDeque;
import java.util.Comparator;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
/**
* This is a compiler pass that computes a control flow graph.
*
*/
final class ControlFlowAnalysis implements Callback, CompilerPass {
/**
* Based roughly on the first few pages of
*
* "Declarative Intraprocedural Flow Analysis of Java Source Code by
* Nilsson-Nyman, Hedin, Magnusson & Ekman",
*
* this pass computes the control flow graph from the AST. However, a full
* attribute grammar is not necessary. We will compute the flow edges with a
* single post order traversal. The "follow()" of a given node will be
* computed recursively in a demand driven fashion.
*
* As of this moment, we are not performing any inter-procedural analysis
* within our framework.
*/
private final AbstractCompiler compiler;
private ControlFlowGraph<Node> cfg;
private Map<Node, Integer> astPosition;
// TODO(nicksantos): should these be node annotations?
private Map<DiGraphNode<Node, Branch>, Integer> nodePriorities;
// We order CFG nodes by by looking at the AST positions.
// CFG nodes that come first lexically should be visited first, because
// they will often be executed first in the source program.
private final Comparator<DiGraphNode<Node, Branch>> priorityComparator =
new Comparator<DiGraphNode<Node, Branch>>() {
@Override
public int compare(
DiGraphNode<Node, Branch> a, DiGraphNode<Node, Branch> b) {
return astPosition.get(a.getValue()) - astPosition.get(b.getValue());
}
};
private int astPositionCounter;
private int priorityCounter;
private final boolean shouldTraverseFunctions;
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> private final boolean edgeAnnotations;
// We need to store where we started, in case we aren't doing a flow analysis
// for the whole scope. This happens, for example, when running type inference
// on only the externs.
private Node root;
/*
* This stack captures the structure of nested TRY blocks. The top of the
* stack is the inner most TRY block. A FUNCTION node in this stack implies
* that the handler is determined by the caller of the function at runtime.
*/
private final Deque<Node> exceptionHandler = new ArrayDeque<Node>();
/*
* This map is used to handle the follow of FINALLY. For example:
*
* while(x) {
* try {
* try {
* break;
* } catch (a) {
* } finally {
* foo();
* }
* fooFollow();
* } catch (b) {
* } finally {
* bar();
* }
* barFollow();
* }
* END();
*
* In this case finallyMap will contain a map from:
* first FINALLY -> bar()
* second FINALLY -> END()
*
* When we are connecting foo() and bar() to to their respective follow, we
* must also look up this map and connect:
* foo() -> bar()
* bar() -> END
*/
private final Multimap<Node, Node> finallyMap = HashMultimap.create();
/**
* Constructor.
*
* @param compiler Compiler instance.
* @param shouldTraverseFunctions Whether functions should be traversed (true
* by default).
* @param edgeAnnotations Whether to allow edge annotations. By default,
* only node annotations are allowed.
*/
ControlFlowAnalysis(AbstractCompiler compiler,
boolean shouldTraverseFunctions, boolean edgeAnnotations) {
this.compiler = compiler;
this.shouldTraverseFunctions = shouldTraverseFunctions;
this.edgeAnnotations = edgeAnnotations;
}
ControlFlowGraph<Node> getCfg() {
return cfg;
}
@Override
public void process(Node externs, Node root) {
this.root = root;
astPositionCounter = 0;
astPosition = Maps.newHashMap();
nodePriorities = Maps.newHashMap();
cfg = new AstControlFlowGraph(computeFallThrough(root), nodePriorities,
edgeAnnotations);
NodeTraversal.traverse(compiler, root, this);
astPosition.put(null, ++astPositionCounter); // the implicit return is last.
// Now, generate the priority of nodes by doing a depth-first
// search on the CFG.
priorityCounter = 0;
DiGraphNode<Node, Branch> entry = cfg.getEntry();
prioritizeFromEntryNode(entry);
if (shouldTraverseFunctions) {
// If we're traversing inner functions, we need to rank the
// priority of them too.
for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) {
Node value = candidate.getValue();
if (value != null && value.isFunction()) {
Preconditions.checkState(
!nodePriorities.containsKey(candidate) || candidate == entry);
prioritizeFromEntryNode
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>(candidate);
}
}
}
// At this point, all reachable nodes have been given a priority, but
// unreachable nodes have not been given a priority. Put them last.
// Presumably, it doesn't really matter what priority they get, since
// this shouldn't happen in real code.
for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) {
if (!nodePriorities.containsKey(candidate)) {
nodePriorities.put(candidate, ++priorityCounter);
}
}
// Again, the implicit return node is always last.
nodePriorities.put(cfg.getImplicitReturn(), ++priorityCounter);
}
/**
* Given an entry node, find all the nodes reachable from that node
* and prioritize them.
*/
private void prioritizeFromEntryNode(DiGraphNode<Node, Branch> entry) {
PriorityQueue<DiGraphNode<Node, Branch>> worklist =
new PriorityQueue<DiGraphNode<Node, Branch>>(10, priorityComparator);
worklist.add(entry);
while (!worklist.isEmpty()) {
DiGraphNode<Node, Branch> current = worklist.remove();
if (nodePriorities.containsKey(current)) {
continue;
}
nodePriorities.put(current, ++priorityCounter);
List<DiGraphNode<Node, Branch>> successors =
cfg.getDirectedSuccNodes(current);
for (DiGraphNode<Node, Branch> candidate : successors) {
worklist.add(candidate);
}
}
}
@Override
public boolean shouldTraverse(
NodeTraversal nodeTraversal, Node n, Node parent) {
astPosition.put(n, astPositionCounter++);
switch (n.getType()) {
case Token.FUNCTION:
if (shouldTraverseFunctions || n == cfg.getEntry().getValue()) {
exceptionHandler.push(n);
return true;
}
return false;
case Token.TRY:
exceptionHandler.push(n);
return true;
}
/*
* We are going to stop the traversal depending on what the node's parent
* is.
*
* We are only interested in adding edges between nodes that change control
* flow. The most obvious ones are loops and IF-ELSE's. A statement
* transfers control to its next sibling.
*
* In case of an expression tree, there is no control flow within the tree
* even when there are short circuited operators and conditionals. When we
* are doing data flow analysis, we will simply synthesize lattices up the
* expression tree by finding the meet at each expression node.
*
* For example: within a Token.SWITCH, the expression in question does not
* change the control flow and need not to be considered.
*/
if (parent != null) {
switch (parent.getType()) {
case Token.FOR:
// Only traverse the body of the for loop.
return n == parent.getLastChild();
// Skip the conditions.
case Token.IF:
case Token.WHILE:
case Token.WITH:
return n != parent.getFirstChild();
case Token.DO:
return n != parent.getFirstChild().getNext();
// Only traverse the body of the cases
case Token.SWITCH:
case
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> Token.CASE:
case Token.CATCH:
case Token.LABEL:
return n != parent.getFirstChild();
case Token.FUNCTION:
return n == parent.getFirstChild().getNext().getNext();
case Token.CONTINUE:
case Token.BREAK:
case Token.EXPR_RESULT:
case Token.VAR:
case Token.RETURN:
case Token.THROW:
return false;
case Token.TRY:
/* Just before we are about to visit the second child of the TRY node,
* we know that we will be visiting either the CATCH or the FINALLY.
* In other words, we know that the post order traversal of the TRY
* block has been finished, no more exceptions can be caught by the
* handler at this TRY block and should be taken out of the stack.
*/
if (n == parent.getFirstChild().getNext()) {
Preconditions.checkState(exceptionHandler.peek() == parent);
exceptionHandler.pop();
}
}
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.IF:
handleIf(n);
return;
case Token.WHILE:
handleWhile(n);
return;
case Token.DO:
handleDo(n);
return;
case Token.FOR:
handleFor(n);
return;
case Token.SWITCH:
handleSwitch(n);
return;
case Token.CASE:
handleCase(n);
return;
case Token.DEFAULT_CASE:
handleDefault(n);
return;
case Token.BLOCK:
case Token.SCRIPT:
handleStmtList(n);
return;
case Token.FUNCTION:
handleFunction(n);
return;
case Token.EXPR_RESULT:
handleExpr(n);
return;
case Token.THROW:
handleThrow(n);
return;
case Token.TRY:
handleTry(n);
return;
case Token.CATCH:
handleCatch(n);
return;
case Token.BREAK:
handleBreak(n);
return;
case Token.CONTINUE:
handleContinue(n);
return;
case Token.RETURN:
handleReturn(n);
return;
case Token.WITH:
handleWith(n);
return;
case Token.LABEL:
return;
default:
handleStmt(n);
return;
}
}
private void handleIf(Node node) {
Node thenBlock = node.getFirstChild().getNext();
Node elseBlock = thenBlock.getNext();
createEdge(node, Branch.ON_TRUE, computeFallThrough(thenBlock));
if (elseBlock == null) {
createEdge(node, Branch.ON_FALSE,
computeFollowNode(node, this)); // not taken branch
} else {
createEdge(node, Branch.ON_FALSE, computeFallThrough(elseBlock));
}
connectToPossibleExceptionHandler(
node, NodeUtil.getConditionExpression(node));
}
private void handleWhile(Node node) {
// Control goes to the first statement if the condition evaluates to true.
createEdge(node, Branch.ON_TRUE,
computeFallThrough(node.getFirstChild().getNext()));
// Control goes to the
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> /**
* A {@link ControlFlowGraph} which provides a node comparator based on the
* pre-order traversal of the AST.
*/
private static class AstControlFlowGraph extends ControlFlowGraph<Node> {
private final Map<DiGraphNode<Node, Branch>, Integer> priorities;
/**
* Constructor.
* @param entry The entry node.
* @param priorities The map from nodes to position in the AST (to be
* filled by the {@link ControlFlowAnalysis#shouldTraverse}).
*/
private AstControlFlowGraph(Node entry,
Map<DiGraphNode<Node, Branch>, Integer> priorities,
boolean edgeAnnotations) {
super(entry,
true /* node annotations */, edgeAnnotations);
this.priorities = priorities;
}
@Override
/**
* Returns a node comparator based on the pre-order traversal of the AST.
* @param isForward x 'before' y in the pre-order traversal implies
* x 'less than' y (if true) and x 'greater than' y (if false).
*/
public Comparator<DiGraphNode<Node, Branch>> getOptionalNodeComparator(
boolean isForward) {
if (isForward) {
return new Comparator<DiGraphNode<Node, Branch>>() {
@Override
public int compare(
DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) {
return getPosition(n1) - getPosition(n2);
}
};
} else {
return new Comparator<DiGraphNode<Node, Branch>>() {
@Override
public int compare(
DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) {
return getPosition(n2) - getPosition(n1);
}
};
}
}
/**
* Gets the pre-order traversal position of the given node.
* @return An arbitrary counter used for comparing positions.
*/
private int getPosition(DiGraphNode<Node, Branch> n) {
Integer priority = priorities.get(n);
Preconditions.checkNotNull(priority);
return priority;
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
private final Set<String> forwardDeclaredTypes = new HashSet<String>();
// A map of properties to the types on which those properties have been
// declared.
private final Map<String, UnionTypeBuilder> typesIndexedByProperty =
Maps.newHashMap();
// A map of properties to each reference type on which those
// properties have been declared. Each type has a unique name used
// for de-duping.
private final Map<String, Map<String, ObjectType>>
eachRefTypeIndexedByProperty = Maps.newHashMap();
// A map of properties to the greatest subtype on which those properties have
// been declared. This is filled lazily from the types declared in
// typesIndexedByProperty.
private final Map<String, JSType> greatestSubtypeByProperty =
Maps.newHashMap();
// A map from interface name to types that implement it.
private final Multimap<String, FunctionType> interfaceToImplementors =
LinkedHashMultimap.create();
// All the unresolved named types.
private final Multimap<StaticScope<JSType>, NamedType> unresolvedNamedTypes =
ArrayListMultimap.create();
// All the resolved named types.
private final Multimap<StaticScope<JSType>, NamedType> resolvedNamedTypes =
ArrayListMultimap.create();
// NamedType warns about unresolved types in the last generation.
private boolean lastGeneration = true;
// The template type name.
private Map<String, TemplateType> templateTypes = Maps.newHashMap();
// A single empty TemplateTypeMap, which can be safely reused in cases where
// there are no template types.
private final TemplateTypeMap emptyTemplateTypeMap;
private final boolean tolerateUndefinedValues;
/**
* Constructs a new type registry populated with the built-in types.
*/
public JSTypeRegistry(ErrorReporter reporter) {
this(reporter, false);
}
/**
* Constructs a new type registry populated with the built-in types.
*/
public JSTypeRegistry(
ErrorReporter reporter, boolean tolerateUndefinedValues) {
this.reporter = reporter;
this.emptyTemplateTypeMap = new TemplateTypeMap(
this, ImmutableList.<TemplateType>of(), ImmutableList.<JSType>of());
nativeTypes = new JSType[JSTypeNative.values().length];
namesToTypes = new HashMap<String, JSType>();
resetForTypeCheck();
this.tolerateUndefinedValues = tolerateUndefinedValues;
}
/**
* @return The template variable corresponding to the property value type for
* Javascript Objects and Arrays.
*/
public TemplateType getObjectElementKey() {
return this.objectElementTemplateKey;
}
/**
* @return The template variable corresponding to the
* property key type of the built-in Javascript object.
*/
public TemplateType getObjectIndexKey() {
Preconditions.checkNotNull(objectIndexTemplateKey);
return objectIndexTemplateKey;
}
public ErrorReporter getErrorReporter() {
return reporter;
}
public boolean shouldTolerateUndefinedValues() {
return tolerateUndefinedValues;
}
/**
* Reset to run the TypeCheck pass.
*/
public void resetForTypeCheck() {
typesIndexedByProperty.clear();
eachRefTypeIndexedByProperty.clear();
initializeBuiltInTypes();
namesToTypes.clear();
namespaces.clear();
initializeRegistry();
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>U2U_FUNCTION_TYPE, U2U_FUNCTION_TYPE);
// unknown constructor type, i.e. (?...) -> ? with the Unknown type
// as instance type
FunctionType U2U_CONSTRUCTOR_TYPE =
// This is equivalent to
// createConstructorType(UNKNOWN_TYPE, true, UNKNOWN_TYPE), but,
// in addition, overrides getInstanceType() to return the NoObject type
// instead of a new anonymous object.
new FunctionType(this, "Function", null,
createArrowType(
createParametersWithVarArgs(UNKNOWN_TYPE),
UNKNOWN_TYPE),
UNKNOWN_TYPE, null, true, true) {
private static final long serialVersionUID = 1L;
@Override public FunctionType getConstructor() {
return registry.getNativeFunctionType(
JSTypeNative.FUNCTION_FUNCTION_TYPE);
}
};
// The U2U_CONSTRUCTOR is weird, because it's the supertype of its
// own constructor.
registerNativeType(JSTypeNative.U2U_CONSTRUCTOR_TYPE, U2U_CONSTRUCTOR_TYPE);
registerNativeType(
JSTypeNative.FUNCTION_INSTANCE_TYPE, U2U_CONSTRUCTOR_TYPE);
FUNCTION_FUNCTION_TYPE.setInstanceType(U2U_CONSTRUCTOR_TYPE);
U2U_CONSTRUCTOR_TYPE.setImplicitPrototype(FUNCTION_PROTOTYPE);
// least function type, i.e. (All...) -> NoType
FunctionType LEAST_FUNCTION_TYPE =
createNativeFunctionTypeWithVarArgs(NO_TYPE, ALL_TYPE);
registerNativeType(JSTypeNative.LEAST_FUNCTION_TYPE, LEAST_FUNCTION_TYPE);
// the 'this' object in the global scope
FunctionType GLOBAL_THIS_CTOR =
new FunctionType(this, "global this", null,
createArrowType(createParameters(false, ALL_TYPE), NUMBER_TYPE),
null, null, true, true);
ObjectType GLOBAL_THIS = GLOBAL_THIS_CTOR.getInstanceType();
registerNativeType(JSTypeNative.GLOBAL_THIS, GLOBAL_THIS);
// greatest function type, i.e. (NoType...) -> All
FunctionType GREATEST_FUNCTION_TYPE =
createNativeFunctionTypeWithVarArgs(ALL_TYPE, NO_TYPE);
registerNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE,
GREATEST_FUNCTION_TYPE);
// Register the prototype property. See the comments below in
// registerPropertyOnType about the bootstrapping process.
registerPropertyOnType("prototype", OBJECT_FUNCTION_TYPE);
}
private void initializeRegistry() {
register(getNativeType(JSTypeNative.ARRAY_TYPE));
register(getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE));
register(getNativeType(JSTypeNative.BOOLEAN_TYPE));
register(getNativeType(JSTypeNative.DATE_TYPE));
register(getNativeType(JSTypeNative.NULL_TYPE));
register(getNativeType(JSTypeNative.NULL_TYPE), "Null");
register(getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE));
register(getNativeType(JSTypeNative.NUMBER_TYPE));
register(getNativeType(JSTypeNative.OBJECT_TYPE));
register(getNative
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Type(JSTypeNative.ERROR_TYPE));
register(getNativeType(JSTypeNative.URI_ERROR_TYPE));
register(getNativeType(JSTypeNative.EVAL_ERROR_TYPE));
register(getNativeType(JSTypeNative.TYPE_ERROR_TYPE));
register(getNativeType(JSTypeNative.RANGE_ERROR_TYPE));
register(getNativeType(JSTypeNative.REFERENCE_ERROR_TYPE));
register(getNativeType(JSTypeNative.SYNTAX_ERROR_TYPE));
register(getNativeType(JSTypeNative.REGEXP_TYPE));
register(getNativeType(JSTypeNative.STRING_OBJECT_TYPE));
register(getNativeType(JSTypeNative.STRING_TYPE));
register(getNativeType(JSTypeNative.VOID_TYPE));
register(getNativeType(JSTypeNative.VOID_TYPE), "Undefined");
register(getNativeType(JSTypeNative.VOID_TYPE), "void");
register(getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), "Function");
}
private void register(JSType type) {
register(type, type.toString());
}
private void register(JSType type, String name) {
Preconditions.checkArgument(
!name.contains("<"), "Type names cannot contain template annotations.");
namesToTypes.put(name, type);
// Add all the namespaces in which this name lives.
while (name.indexOf('.') > 0) {
name = name.substring(0, name.lastIndexOf('.'));
namespaces.add(name);
}
}
private void registerNativeType(JSTypeNative typeId, JSType type) {
nativeTypes[typeId.ordinal()] = type;
}
/**
* Tells the type system that {@code owner} may have a property named
* {@code propertyName}. This allows the registry to keep track of what
* types a property is defined upon.
*
* This is NOT the same as saying that {@code owner} must have a property
* named type. ObjectType#hasProperty attempts to minimize false positives
* ("if we're not sure, then don't type check this property"). The type
* registry, on the other hand, should attempt to minimize false negatives
* ("if this property is assigned anywhere in the program, it must
* show up in the type registry").
*/
public void registerPropertyOnType(String propertyName, JSType type) {
UnionTypeBuilder typeSet = typesIndexedByProperty.get(propertyName);
if (typeSet == null) {
typeSet = new UnionTypeBuilder(this, PROPERTY_CHECKING_UNION_SIZE);
typesIndexedByProperty.put(propertyName, typeSet);
}
typeSet.addAlternate(type);
addReferenceTypeIndexedByProperty(propertyName, type);
// Clear cached values that depend on typesIndexedByProperty.
greatestSubtypeByProperty.remove(propertyName);
}
private void addReferenceTypeIndexedByProperty(
String propertyName, JSType type) {
if (type instanceof ObjectType && ((ObjectType) type).hasReferenceName()) {
Map<String, ObjectType> typeSet =
eachRefTypeIndexedByProperty.get(propertyName);
if (typeSet == null) {
typeSet = Maps.newHashMap();
eachRefTypeIndexedByProperty.put(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
interfaceToImplementors.put(interfaceInstance.getReferenceName(), type);
}
/**
* Returns a collection of types that directly implement {@code
* interfaceInstance}. Subtypes of implementing types are not guaranteed to
* be returned. {@code interfaceInstance} must be an ObjectType for the
* instance of the interface.
*/
public Collection<FunctionType> getDirectImplementors(
ObjectType interfaceInstance) {
return interfaceToImplementors.get(interfaceInstance.getReferenceName());
}
/**
* Records declared global type names. This makes resolution faster
* and more robust in the common case.
*
* @param name The name of the type to be recorded.
* @param t The actual type being associated with the name.
* @return True if this name is not already defined, false otherwise.
*/
public boolean declareType(String name, JSType t) {
if (namesToTypes.containsKey(name)) {
return false;
}
register(t, name);
return true;
}
/**
* Overrides a declared global type name. Throws an exception if this
* type name hasn't been declared yet.
*/
public void overwriteDeclaredType(String name, JSType t) {
Preconditions.checkState(namesToTypes.containsKey(name));
register(t, name);
}
/**
* Records a forward-declared type name. We will not emit errors if this
* type name never resolves to anything.
*/
public void forwardDeclareType(String name) {
forwardDeclaredTypes.add(name);
}
/**
* Whether this is a forward-declared type name.
*/
public boolean isForwardDeclaredType(String name) {
return forwardDeclaredTypes.contains(name);
}
/** Determines whether the given JS package exists. */
public boolean hasNamespace(String name) {
return namespaces.contains(name);
}
/**
* Looks up a type by name.
*
* @param jsTypeName The name string.
* @return the corresponding JSType object or {@code null} it cannot be found
*/
public JSType getType(String jsTypeName) {
// TODO(user): Push every local type name out of namesToTypes so that
// NamedType#resolve is correct.
TemplateType templateType = templateTypes.get(jsTypeName);
if (templateType != null) {
return templateType;
}
return namesToTypes.get(jsTypeName);
}
public JSType getNativeType(JSTypeNative typeId) {
return nativeTypes[typeId.ordinal()];
}
public ObjectType getNativeObjectType(JSTypeNative typeId) {
return (ObjectType) getNativeType(typeId);
}
public FunctionType getNativeFunctionType(JSTypeNative typeId) {
return (FunctionType) getNativeType(typeId);
}
/**
* Looks up a type by name. To allow for forward references to types, an
* unrecognized string has to be bound to a NamedType object that will be
* resolved later.
*
* @param scope A scope for doing type name resolution.
* @param jsTypeName The name string.
* @param sourceName The name of the source file where this reference appears.
* @param lineno The line number of the reference.
* @return a NamedType if the string argument
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> some reason. There are
* a few different reasons why this could fail: for example, numbers
* can't be implicit prototypes, and we don't want to change the implicit
* prototype if other classes have already subclassed this one.
*/
public boolean resetImplicitPrototype(
JSType type, ObjectType newImplicitProto) {
if (type instanceof PrototypeObjectType) {
PrototypeObjectType poType = (PrototypeObjectType) type;
poType.clearCachedValues();
poType.setImplicitPrototype(newImplicitProto);
return true;
}
return false;
}
/**
* Create an anonymous object type for a native type.
*/
ObjectType createNativeAnonymousObjectType() {
PrototypeObjectType type =
new PrototypeObjectType(this, null, null, true, null);
type.setPrettyPrint(true);
return type;
}
/**
* Creates a constructor function type.
* @param name the function's name or {@code null} to indicate that the
* function is anonymous.
* @param source the node defining this function. Its type
* ({@link Node#getType()}) must be {@link Token#FUNCTION}.
* @param parameters the function's parameters or {@code null}
* to indicate that the parameter types are unknown.
* @param returnType the function's return type or {@code null} to indicate
* that the return type is unknown.
* @param templateKeys the templatized types for the class.
*/
public FunctionType createConstructorType(String name, Node source,
Node parameters, JSType returnType, ImmutableList<TemplateType> templateKeys) {
return new FunctionType(this, name, source,
createArrowType(parameters, returnType), null,
createTemplateTypeMap(templateKeys, null), true, false);
}
ImmutableList<TemplateType> createTemplateMapKeys(ImmutableList<String> keys) {
ImmutableList.Builder<TemplateType> builder = ImmutableList.builder();
if (keys != null) {
for (String key : keys) {
builder.add(new TemplateType(this, key));
}
}
return builder.build();
}
/**
* Creates an interface function type.
* @param name the function's name
* @param source the node defining this function. Its type
* ({@link Node#getType()}) must be {@link Token#FUNCTION}.
* @param templateKeys the templatized types for the interface.
*/
public FunctionType createInterfaceType(String name, Node source,
ImmutableList<TemplateType> templateKeys) {
return FunctionType.forInterface(this, name, source,
createTemplateTypeMap(templateKeys, null));
}
public TemplateType createTemplateType(String name) {
return new TemplateType(this, name);
}
/**
* Creates a template type map from the specified list of template keys and
* template value types.
*/
public TemplateTypeMap createTemplateTypeMap(
ImmutableList<TemplateType> templateKeys,
ImmutableList<JSType> templateValues) {
templateKeys = templateKeys == null ?
ImmutableList.<TemplateType>of() : templateKeys;
templateValues = templateValues == null ?
ImmutableList.<JSType>of() : templateValues;
return (templateKeys.isEmpty() && templateValues.isEmpty()) ?
emptyTemplateTypeMap :
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
new TemplateTypeMap(this, templateKeys, templateValues);
}
/**
* Creates a templatized instance of the specified type. Only ObjectTypes
* can currently be templatized; extend the logic in this function when
* more types can be templatized.
* @param baseType the type to be templatized.
* @param templatizedTypes a list of the template JSTypes. Will be matched by
* list order to the template keys on the base type.
*/
public TemplatizedType createTemplatizedType(
ObjectType baseType, ImmutableList<JSType> templatizedTypes) {
// Only ObjectTypes can currently be templatized; extend this logic when
// more types can be templatized.
return new TemplatizedType(this, baseType, templatizedTypes);
}
/**
* Creates a templatized instance of the specified type. Only ObjectTypes
* can currently be templatized; extend the logic in this function when
* more types can be templatized.
* @param baseType the type to be templatized.
* @param templatizedTypes a map from TemplateType to corresponding JSType
* value. Any unfilled TemplateTypes on the baseType that are *not*
* contained in this map will have UNKNOWN_TYPE used as their value.
*/
public TemplatizedType createTemplatizedType(
ObjectType baseType, Map<TemplateType, JSType> templatizedTypes) {
ImmutableList.Builder<JSType> builder = ImmutableList.builder();
TemplateTypeMap baseTemplateTypeMap = baseType.getTemplateTypeMap();
for (TemplateType key : baseTemplateTypeMap.getUnfilledTemplateKeys()) {
JSType templatizedType = templatizedTypes.containsKey(key) ?
templatizedTypes.get(key) : getNativeType(UNKNOWN_TYPE);
builder.add(templatizedType);
}
return createTemplatizedType(baseType, builder.build());
}
/**
* Creates a templatized instance of the specified type. Only ObjectTypes
* can currently be templatized; extend the logic in this function when
* more types can be templatized.
* @param baseType the type to be templatized.
* @param templatizedTypes a list of the template JSTypes. Will be matched by
* list order to the template keys on the base type.
*/
public TemplatizedType createTemplatizedType(
ObjectType baseType, JSType... templatizedTypes) {
return createTemplatizedType(
baseType, ImmutableList.copyOf(templatizedTypes));
}
/**
* Creates a named type.
*/
@VisibleForTesting
public NamedType createNamedType(String reference,
String sourceName, int lineno, int charno) {
if (reference.endsWith(".")) {
return new NamespaceType(this, reference, sourceName, lineno, charno);
} else {
return new NamedType(this, reference, sourceName, lineno, charno);
}
}
/**
* Identifies the name of a typedef or enum before we actually declare it.
*/
public void identifyNonNullableName(String name) {
Preconditions.checkNotNull(name);
nonNullableTypeNames.add(name);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
}
/**
* Creates a JSType from the nodes representing a type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
public JSType createFromTypeNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
return createFromTypeNodesInternal(n, sourceName, scope);
}
private boolean hasTypeName(Node n) {
if (n.getType() == Token.STRING) {
return true;
}
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
if (hasTypeName(child)) {
return true;
}
}
return false;
}
/** @see #createFromTypeNodes(Node, String, StaticScope) */
private JSType createFromTypeNodesInternal(Node n, String sourceName,
StaticScope<JSType> scope) {
switch (n.getType()) {
case Token.LC: // Record type.
return createRecordTypeFromNodes(
n.getFirstChild(), sourceName, scope);
case Token.BANG: // Not nullable
return createFromTypeNodesInternal(
n.getFirstChild(), sourceName, scope)
.restrictByNotNullOrUndefined();
case Token.QMARK: // Nullable or unknown
Node firstChild = n.getFirstChild();
if (firstChild == null) {
return getNativeType(UNKNOWN_TYPE);
}
return createDefaultObjectUnion(
createFromTypeNodesInternal(
firstChild, sourceName, scope));
case Token.EQUALS: // Optional
return createOptionalType(
createFromTypeNodesInternal(
n.getFirstChild(), sourceName, scope));
case Token.ELLIPSIS: // Var args
return createOptionalType(
createFromTypeNodesInternal(
n.getFirstChild(), sourceName, scope));
case Token.STAR: // The AllType
return getNativeType(ALL_TYPE);
case Token.LB: // Array type
// TODO(nicksantos): Enforce membership restrictions on the Array.
return getNativeType(ARRAY_TYPE);
case Token.PIPE: // Union type
UnionTypeBuilder builder = new UnionTypeBuilder(this);
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
builder.addAlternate(
createFromTypeNodesInternal(child, sourceName, scope));
}
return builder.build();
case Token.EMPTY: // When the return value of a function is not specified
return getNativeType(UNKNOWN_TYPE);
case Token.VOID: // Only allowed in the return value of a function.
return getNativeType(VOID_TYPE);
case Token.STRING:
JSType namedType = getType(scope, n.getString(), sourceName,
n.getLineno(), n.getCharno());
if ((namedType instanceof ObjectType) &&
!(namedType instanceof NamespaceType) &&
!(nonNullableTypeNames.contains(n.getString()))) {
Node typeList = n.getFirstChild();
int nAllowedTypes =
namedType.getTemplateTypeMap().numUnfilledTemplateKeys();
if (typeList != null && nAllowedTypes > 0) {
// Templatized types.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>"),
sourceName, arg.getLineno(), arg.getCharno());
}
} else {
paramBuilder.addRequiredParams(type);
}
}
}
current = current.getNext();
}
JSType returnType =
createFromTypeNodesInternal(current, sourceName, scope);
return new FunctionBuilder(this)
.withParams(paramBuilder)
.withReturnType(returnType)
.withTypeOfThis(thisType)
.setIsConstructor(isConstructor)
.build();
}
throw new IllegalStateException(
"Unexpected node in type expression: " + n.toString());
}
/**
* Creates a RecordType from the nodes representing said record type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
private JSType createRecordTypeFromNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
RecordTypeBuilder builder = new RecordTypeBuilder(this);
// For each of the fields in the record type.
for (Node fieldTypeNode = n.getFirstChild();
fieldTypeNode != null;
fieldTypeNode = fieldTypeNode.getNext()) {
// Get the property's name.
Node fieldNameNode = fieldTypeNode;
boolean hasType = false;
if (fieldTypeNode.getType() == Token.COLON) {
fieldNameNode = fieldTypeNode.getFirstChild();
hasType = true;
}
String fieldName = fieldNameNode.getString();
// TODO(user): Move this into the lexer/parser.
// Remove the string literal characters around a field name,
// if any.
if (fieldName.startsWith("'") || fieldName.startsWith("\"")) {
fieldName = fieldName.substring(1, fieldName.length() - 1);
}
// Get the property's type.
JSType fieldType = null;
if (hasType) {
// We have a declared type.
fieldType = createFromTypeNodesInternal(
fieldTypeNode.getLastChild(), sourceName, scope);
} else {
// Otherwise, the type is UNKNOWN.
fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
// Add the property to the record.
if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) {
// Duplicate field name, warning and skip
reporter.warning(
"Duplicate record field " + fieldName,
sourceName,
n.getLineno(), fieldNameNode.getCharno());
}
}
return builder.build();
}
/**
* Sets the template type name.
*/
public void setTemplateTypeNames(List<TemplateType> keys) {
Preconditions.checkNotNull(keys);
for (TemplateType key : keys) {
templateTypes.put(key.getReferenceName(), key);
}
}
/**
* Clears the template type name.
*/
public void clearTemplateTypeNames() {
templateTypes.clear();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> null (in particular, if not overridden)
*/
public Comparator<DiGraphNode<N, Branch>> getOptionalNodeComparator(
boolean isForward) {
return null;
}
/**
* The edge object for the control flow graph.
*/
public static enum Branch {
/** Edge is taken if the condition is true. */
ON_TRUE,
/** Edge is taken if the condition is false. */
ON_FALSE,
/** Unconditional branch. */
UNCOND,
/**
* Exception-handling code paths.
* Conflates two kind of control flow passing:
* - An exception is thrown, and falls into a catch or finally block
* - During exception handling, a finally block finishes and control
* passes to the next finally block.
* In theory, we need 2 different edge types. In practice, we
* can just treat them as "the edges we can't really optimize".
*/
ON_EX,
/** Possible folded-away template */
SYN_BLOCK;
public boolean isConditional() {
return this == ON_TRUE || this == ON_FALSE;
}
}
/**
* Abstract callback to visit a control flow graph node without going into
* subtrees of the node that are also represented by other
* control flow graph nodes.
*
* <p>For example, traversing an IF node as root will visit the two subtrees
* pointed by the {@link ControlFlowGraph.Branch#ON_TRUE} and
* {@link ControlFlowGraph.Branch#ON_FALSE} edges.
*/
public abstract static class AbstractCfgNodeTraversalCallback implements
Callback {
@Override
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
if (parent == null) {
return true;
}
return !isEnteringNewCfgNode(n);
}
}
/**
* @return True if n should be represented by a new CFG node in the control
* flow graph.
*/
public static boolean isEnteringNewCfgNode(Node n) {
Node parent = n.getParent();
switch (parent.getType()) {
case Token.BLOCK:
case Token.SCRIPT:
case Token.TRY:
return true;
case Token.FUNCTION:
// A function node represents the start of a function where the name
// bleeds into the local scope and parameters are assigned
// to the formal argument names. The node includes the name of the
// function and the LP list since we assume the whole set up process
// is atomic without change in control flow. The next change of
// control is going into the function's body, represented by the second
// child.
return n != parent.getFirstChild().getNext();
case Token.WHILE:
case Token.DO:
case Token.IF:
// These control structures are represented by a node that holds the
// condition. Each of them is a branch node based on its condition.
return NodeUtil.getConditionExpression(parent) != n;
case Token.FOR:
// The FOR(;;) node differs from other control structures in that
// it has an initialization and an increment statement. Those
// two statements have corresponding CFG nodes to represent them.
// The FOR node only represents the condition check for each iteration.
// That way the following:
// for(var
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> x = 0; x < 10; x++) { } has a graph that is isomorphic to
// var x = 0; while(x<10) { x++; }
if (NodeUtil.isForIn(parent)) {
// TODO(user): Investigate how we should handle the case where
// we have a very complex expression inside the FOR-IN header.
return n != parent.getFirstChild();
} else {
return NodeUtil.getConditionExpression(parent) != n;
}
case Token.SWITCH:
case Token.CASE:
case Token.CATCH:
case Token.WITH:
return n != parent.getFirstChild();
default:
return false;
}
}
@Override
public String toString() {
String s = "CFG:\n";
for (GraphvizEdge e : getGraphvizEdges()) {
s += e.toString() + '\n';
}
return s;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>CAST_TO_VISITOR =
new CanCastToVisitor();
public static final String UNKNOWN_NAME =
"Unknown class name";
public static final String NOT_A_CLASS =
"Not declared as a constructor";
public static final String NOT_A_TYPE =
"Not declared as a type name";
public static final String EMPTY_TYPE_COMPONENT =
"Named type with empty name component";
/**
* Total ordering on types based on their textual representation.
* This is used to have a deterministic output of the toString
* method of the union type since this output is used in tests.
*/
static final Comparator<JSType> ALPHA = new Comparator<JSType>() {
@Override
public int compare(JSType t1, JSType t2) {
return t1.toString().compareTo(t2.toString());
}
};
// A flag set on enum definition tree nodes
public static final int ENUMDECL = 1;
public static final int NOT_ENUMDECL = 0;
final JSTypeRegistry registry;
JSType(JSTypeRegistry registry) {
this(registry, null);
}
JSType(JSTypeRegistry registry, TemplateTypeMap templateTypeMap) {
this.registry = registry;
this.templateTypeMap = templateTypeMap == null ?
registry.createTemplateTypeMap(null, null) : templateTypeMap;
}
/**
* Utility method for less verbose code.
*/
JSType getNativeType(JSTypeNative typeId) {
return registry.getNativeType(typeId);
}
/**
* Gets the docInfo for this type. By default, documentation cannot be
* attached to arbitrary types. This must be overridden for
* programmer-defined types.
*/
public JSDocInfo getJSDocInfo() {
return null;
}
/**
* Returns a user meaningful label for the JSType instance. For example,
* Functions and Enums will return their declaration name (if they have one).
* Some types will not have a meaningful display name. Calls to
* hasDisplayName() will return true IFF getDisplayName() will return null
* or a zero length string.
*
* @return the display name of the type, or null if one is not available
*/
public String getDisplayName() {
return null;
}
/**
* @return true if the JSType has a user meaningful label.
*/
public boolean hasDisplayName() {
String displayName = getDisplayName();
return displayName != null && !displayName.isEmpty();
}
/**
* Checks whether the property is present on the object.
* @param pname The property name.
*/
public boolean hasProperty(String pname) {
return false;
}
public boolean isNoType() {
return false;
}
public boolean isNoResolvedType() {
return false;
}
public boolean isNoObjectType() {
return false;
}
public final boolean isEmptyType() {
return isNoType() || isNoObjectType() || isNoResolvedType() ||
(registry.getNativeFunctionType(
JSTypeNative.LEAST_FUNCTION_TYPE) == this);
}
public boolean isNumberObjectType() {
return false;
}
public boolean isNumberValueType() {
return false;
}
/** Whether this is the prototype of a function. */
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> }
public static boolean isEquivalent(JSType typeA, JSType typeB) {
return (typeA == null || typeB == null) ?
typeA == typeB : typeA.isEquivalentTo(typeB);
}
@Override
public boolean equals(Object jsType) {
return (jsType instanceof JSType) ?
isEquivalentTo((JSType) jsType) : false;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
/**
* This predicate is used to test whether a given type can appear in a
* 'Int32' context. This context includes, for example, the operands of a
* bitwise or operator. Since we do not currently support integer types,
* this is a synonym for {@code Number}.
*/
public final boolean matchesInt32Context() {
return matchesNumberContext();
}
/**
* This predicate is used to test whether a given type can appear in a
* 'Uint32' context. This context includes the right-hand operand of a shift
* operator.
*/
public final boolean matchesUint32Context() {
return matchesNumberContext();
}
/**
* This predicate is used to test whether a given type can appear in a
* numeric context, such as an operand of a multiply operator.
*/
public boolean matchesNumberContext() {
return false;
}
/**
* This predicate is used to test whether a given type can appear in a
* {@code String} context, such as an operand of a string concat (+) operator.
*
* All types have at least the potential for converting to {@code String}.
* When we add externally defined types, such as a browser OM, we may choose
* to add types that do not automatically convert to {@code String}.
*/
public boolean matchesStringContext() {
return false;
}
/**
* This predicate is used to test whether a given type can appear in an
* {@code Object} context, such as the expression in a with statement.
*
* Most types we will encounter, except notably {@code null}, have at least
* the potential for converting to {@code Object}. Host defined objects can
* get peculiar.
*/
public boolean matchesObjectContext() {
return false;
}
/**
* Coerces this type to an Object type, then gets the type of the property
* whose name is given.
*
* Unlike {@link ObjectType#getPropertyType}, returns null if the property
* is not found.
*
* @return The property's type. {@code null} if the current type cannot
* have properties, or if the type is not found.
*/
public JSType findPropertyType(String propertyName) {
ObjectType autoboxObjType = ObjectType.cast(autoboxesTo());
if (autoboxObjType != null) {
return autoboxObjType.findPropertyType(propertyName);
}
return null;
}
/**
* This predicate is used to test whether a given type can be used as the
* 'function' in a function call.
*
* @return {@code true} if this type might be callable.
*/
public boolean canBeCalled() {
return false;
}
/**
* Tests whether values of {@code
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> Any Type if the underlying type could
* not have yielded this ToBoolean value
*
* TODO(user): Move this method to the SemanticRAI and use the visit
* method of types to get the restricted type.
*/
public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) {
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
BooleanLiteralSet literals = getPossibleToBooleanOutcomes();
if (literals.contains(outcome)) {
return this;
} else {
return getNativeType(JSTypeNative.NO_TYPE);
}
}
/**
* Computes the set of possible outcomes of the {@code ToBoolean} predicate
* for this type. The {@code ToBoolean} predicate is defined by the ECMA-262
* standard, 3<sup>rd</sup> edition. Its behavior for simple types can be
* summarized by the following table:
* <table>
* <tr><th>type</th><th>result</th></tr>
* <tr><td>{@code undefined}</td><td>{false}</td></tr>
* <tr><td>{@code null}</td><td>{false}</td></tr>
* <tr><td>{@code boolean}</td><td>{true, false}</td></tr>
* <tr><td>{@code number}</td><td>{true, false}</td></tr>
* <tr><td>{@code string}</td><td>{true, false}</td></tr>
* <tr><td>{@code Object}</td><td>{true}</td></tr>
* </table>
* @return the set of boolean literals for this type
*/
public abstract BooleanLiteralSet getPossibleToBooleanOutcomes();
/**
* Computes the subset of {@code this} and {@code that} types if equality
* is observed. If a value {@code v1} of type {@code null} is equal to a value
* {@code v2} of type {@code (undefined,number)}, we can infer that the
* type of {@code v1} is {@code null} and the type of {@code v2} is
* {@code undefined}.
*
* @return a pair containing the restricted type of {@code this} as the first
* component and the restricted type of {@code that} as the second
* element. The returned pair is never {@code null} even though its
* components may be {@code null}
*/
public TypePair getTypesUnderEquality(JSType that) {
// unions types
if (that.isUnionType()) {
TypePair p = that.toMaybeUnionType().getTypesUnderEquality(this);
return new TypePair(p.typeB, p.typeA);
}
// other types
switch (testForEquality(that)) {
case FALSE:
return new TypePair(null, null);
case TRUE:
case UNKNOWN:
return new TypePair(this, that);
}
// switch case is exhaustive
throw new IllegalStateException();
}
/**
* Computes the subset of {@code this} and {@code that} types if inequality
* is observed. If a value {@code v
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> // values are subtypes/supertypes of one another.
TemplateType key = thisType.registry.getObjectElementKey();
JSType thisElement = thisTypeParams.getTemplateType(key);
JSType thatElement = thatTypeParams.getTemplateType(key);
templateMatch = thisElement.isSubtype(thatElement)
|| thatElement.isSubtype(thisElement);
} else {
templateMatch = thisTypeParams.checkEquivalenceHelper(
thatTypeParams, EquivalenceMethod.INVARIANT);
}
if (!templateMatch) {
return false;
}
// Templatized types. The above check guarantees TemplateTypeMap
// equivalence; check if the base type is a subtype.
if (thisType.isTemplatizedType()) {
return thisType.toMaybeTemplatizedType().getReferencedType().isSubtype(
thatType);
}
// proxy types
if (thatType instanceof ProxyObjectType) {
return thisType.isSubtype(
((ProxyObjectType) thatType).getReferencedTypeInternal());
}
return false;
}
/**
* Determines if the specified type is exempt from standard invariant
* templatized typing rules.
*/
static boolean isExemptFromTemplateTypeInvariance(JSType type) {
ObjectType objType = type.toObjectType();
return objType == null ||
"Array".equals(objType.getReferenceName()) ||
"Object".equals(objType.getReferenceName());
}
/**
* Visit this type with the given visitor.
* @see com.google.javascript.rhino.jstype.Visitor
* @return the value returned by the visitor
*/
public abstract <T> T visit(Visitor<T> visitor);
/**
* Visit the types with the given visitor.
* @see com.google.javascript.rhino.jstype.RelationshipVisitor
* @return the value returned by the visitor
*/
abstract <T> T visit(RelationshipVisitor<T> visitor, JSType that);
/**
* Resolve this type in the given scope.
*
* The returned value must be equal to {@code this}, as defined by
* {@link #isEquivalentTo}. It may or may not be the same object. This method
* may modify the internal state of {@code this}, as long as it does
* so in a way that preserves Object equality.
*
* For efficiency, we should only resolve a type once per compilation job.
* For incremental compilations, one compilation job may need the
* artifacts from a previous generation, so we will eventually need
* a generational flag instead of a boolean one.
*/
public final JSType resolve(ErrorReporter t, StaticScope<JSType> scope) {
if (resolved) {
// TODO(nicksantos): Check to see if resolve() looped back on itself.
// Preconditions.checkNotNull(resolveResult);
if (resolveResult == null) {
return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
return resolveResult;
}
resolved = true;
resolveResult = resolveInternal(t, scope);
resolveResult.setResolvedTypeInternal(resolveResult);
return resolveResult;
}
/**
* @see #resolve
*/
abstract JSType resolveInternal(ErrorReporter t, StaticScope<JSType>
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> scope);
void setResolvedTypeInternal(JSType type) {
resolveResult = type;
resolved = true;
}
/** Whether the type has been resolved. */
public final boolean isResolved() {
return resolved;
}
/** Clears the resolved field. */
public final void clearResolved() {
resolved = false;
resolveResult = null;
}
/**
* A null-safe resolve.
* @see #resolve
*/
static final JSType safeResolve(
JSType type, ErrorReporter t, StaticScope<JSType> scope) {
return type == null ? null : type.resolve(t, scope);
}
/**
* Certain types have constraints on them at resolution-time.
* For example, a type in an {@code @extends} annotation must be an
* object. Clients should inject a validator that emits a warning
* if the type does not validate, and return false.
*/
public boolean setValidator(Predicate<JSType> validator) {
return validator.apply(this);
}
public static class TypePair {
public final JSType typeA;
public final JSType typeB;
public TypePair(JSType typeA, JSType typeB) {
this.typeA = typeA;
this.typeB = typeB;
}
}
/**
* A string representation of this type, suitable for printing
* in warnings.
*/
@Override
public String toString() {
return toStringHelper(false);
}
/**
* A hash code function for diagnosing complicated issues
* around type-identity.
*/
public String toDebugHashCodeString() {
return "{" + hashCode() + "}";
}
/**
* A string representation of this type, suitable for printing
* in type annotations at code generation time.
*/
public final String toAnnotationString() {
return toStringHelper(true);
}
/**
* @param forAnnotations Whether this is for use in code generator
* annotations. Otherwise, it's for warnings.
*/
abstract String toStringHelper(boolean forAnnotations);
/**
* Modify this type so that it matches the specified type.
*
* This is useful for reverse type-inference, where we want to
* infer that an object literal matches its constraint (much like
* how the java compiler does reverse-inference to figure out generics).
* @param constraint
*/
public void matchConstraint(JSType constraint) {}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node;
/**
* The arrow type is an internal type that models the functional arrow type
* seen in typical functional programming languages. It is used solely for
* separating the management of the arrow type from the complex
* {@link FunctionType} that models JavaScript's notion of functions.
*/
final class ArrowType extends JSType {
private static final long serialVersionUID = 1L;
final Node parameters;
JSType returnType;
// Whether the return type is inferred.
final boolean returnTypeInferred;
ArrowType(JSTypeRegistry registry, Node parameters,
JSType returnType) {
this(registry, parameters, returnType, false);
}
ArrowType(JSTypeRegistry registry, Node parameters,
JSType returnType, boolean returnTypeInferred) {
super(registry);
this.parameters = parameters == null ?
registry.createParametersWithVarArgs(getNativeType(UNKNOWN_TYPE)) :
parameters;
this.returnType = returnType == null ?
getNativeType(UNKNOWN_TYPE) : returnType;
this.returnTypeInferred = returnTypeInferred;
}
@Override
public boolean isSubtype(JSType other) {
if (!(other instanceof ArrowType)) {
return false;
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> null;
thatParam = null;
}
}
// "that" can't be a supertype, because it's missing a required argument.
if (thisParam != null
&& !thisParam.isOptionalArg() && !thisParam.isVarArgs()
&& thatParam == null) {
return false;
}
return true;
}
/**
* @return True if our parameter spec is equal to {@code that}'s parameter
* spec.
*/
boolean hasEqualParameters(ArrowType that, EquivalenceMethod eqMethod) {
Node thisParam = parameters.getFirstChild();
Node otherParam = that.parameters.getFirstChild();
while (thisParam != null && otherParam != null) {
JSType thisParamType = thisParam.getJSType();
JSType otherParamType = otherParam.getJSType();
if (thisParamType != null) {
// Both parameter lists give a type for this param, it should be equal
if (otherParamType != null &&
!thisParamType.checkEquivalenceHelper(
otherParamType, eqMethod)) {
return false;
}
} else {
if (otherParamType != null) {
return false;
}
}
// Check var_args/optionality
if (thisParam.isOptionalArg() != otherParam.isOptionalArg()) {
return false;
}
if (thisParam.isVarArgs() != otherParam.isVarArgs()) {
return false;
}
thisParam = thisParam.getNext();
otherParam = otherParam.getNext();
}
// One of the parameters is null, so the types are only equal if both
// parameter lists are null (they are equal).
return thisParam == otherParam;
}
boolean checkArrowEquivalenceHelper(
ArrowType that, EquivalenceMethod eqMethod) {
// Please keep this method in sync with the hashCode() method below.
if (!returnType.checkEquivalenceHelper(that.returnType, eqMethod)) {
return false;
}
return hasEqualParameters(that, eqMethod);
}
@Override
public int hashCode() {
int hashCode = 0;
if (returnType != null) {
hashCode += returnType.hashCode();
}
if (returnTypeInferred) {
hashCode += 1;
}
if (parameters != null) {
Node param = parameters.getFirstChild();
while (param != null) {
JSType paramType = param.getJSType();
if (paramType != null) {
hashCode += paramType.hashCode();
}
param = param.getNext();
}
}
return hashCode;
}
@Override
public JSType getLeastSupertype(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public JSType getGreatestSubtype(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public TernaryValue testForEquality(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public <T> T visit(Visitor<T> visitor) {
throw new UnsupportedOperationException();
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
throw new UnsupportedOperationException();
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.TRUE
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
returnType = safeResolve(returnType, t, scope);
if (parameters != null) {
for (Node paramNode = parameters.getFirstChild();
paramNode != null; paramNode = paramNode.getNext()) {
paramNode.setJSType(paramNode.getJSType().resolve(t, scope));
}
}
return this;
}
boolean hasUnknownParamsOrReturn() {
if (parameters != null) {
for (Node paramNode = parameters.getFirstChild();
paramNode != null; paramNode = paramNode.getNext()) {
JSType type = paramNode.getJSType();
if (type == null || type.isUnknownType()) {
return true;
}
}
}
return returnType == null || returnType.isUnknownType();
}
@Override
String toStringHelper(boolean forAnnotations) {
return "[ArrowType]";
}
@Override
public boolean hasAnyTemplateTypesInternal() {
return returnType.hasAnyTemplateTypes()
|| hasTemplatedParameterType();
}
private boolean hasTemplatedParameterType() {
if (parameters != null) {
for (Node paramNode = parameters.getFirstChild();
paramNode != null; paramNode = paramNode.getNext()) {
JSType type = paramNode.getJSType();
if (type != null && type.hasAnyTemplateTypes()) {
return true;
}
}
}
return false;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> that the {@link Visibility#compareTo} method can be used to
* determine if a visibility is more permissive than another.
*/
public enum Visibility {
PRIVATE,
PROTECTED,
PUBLIC,
// If visibility is not specified, we just assume that visibility
// is inherited from the super class.
INHERITED
}
private static final class LazilyInitializedInfo implements Serializable {
private static final long serialVersionUID = 1L;
// Function information
JSTypeExpression baseType = null;
List<JSTypeExpression> extendedInterfaces = null;
List<JSTypeExpression> implementedInterfaces = null;
Map<String, JSTypeExpression> parameters = null;
List<JSTypeExpression> thrownTypes = null;
ImmutableList<String> templateTypeNames = null;
Set<String> disposedParameters = null;
// Other information
String description = null;
String meaning = null;
String deprecated = null;
String license = null;
Set<String> suppressions = null;
Set<String> modifies = null;
String lendsName = null;
// TODO(nnaze): Consider converting the boolean flags to bit fields.
boolean ngInject = false;
boolean wizaction = false;
// Tags for Jagger dependency injection prototype
boolean jaggerInject = false;
boolean jaggerProvide = false;
boolean jaggerModule = false;
}
private static final class LazilyInitializedDocumentation {
String sourceComment = null;
List<Marker> markers = null;
Map<String, String> parameters = null;
Map<JSTypeExpression, String> throwsDescriptions = null;
String blockDescription = null;
String fileOverview = null;
String returnDescription = null;
String version = null;
List<String> authors = null;
List<String> sees = null;
}
/**
* A piece of information (found in a marker) which contains a position
* with a string.
*/
public static class StringPosition extends SourcePosition<String> {
}
/**
* A piece of information (found in a marker) which contains a position
* with a string that has no leading or trailing whitespace.
*/
static class TrimmedStringPosition extends StringPosition {
@Override public void setItem(String item) {
Preconditions.checkArgument(
item.charAt(0) != ' ' &&
item.charAt(item.length() - 1) != ' ',
"String has leading or trailing whitespace");
super.setItem(item);
}
}
/**
* A piece of information (found in a marker) which contains a position
* with a name node.
*/
public static class NamePosition extends SourcePosition<Node> {}
/**
* A piece of information (found in a marker) which contains a position
* with a type expression syntax tree.
*/
public static class TypePosition extends SourcePosition<Node> {
private boolean brackets = false;
/** Returns whether the type has curly braces around it. */
public boolean hasBrackets() {
return brackets;
}
void setHasBrackets(boolean newVal) {
brackets = newVal;
}
}
/**
* Defines a class for containing the parsing information
* for this JSDocInfo. For each annotation found in the
* JsDoc, a marker will be created indicating
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
void setJaggerProvide(boolean jaggerProvide) {
lazyInitInfo();
info.jaggerProvide = jaggerProvide;
}
/**
* Returns whether JSDoc is annotated with {@code @jaggerModule} annotation.
*/
public boolean isJaggerModule() {
return (info != null) && info.jaggerModule;
}
void setJaggerModule(boolean jaggerModule) {
lazyInitInfo();
info.jaggerModule = jaggerModule;
}
/**
* Returns whether JSDoc is annotated with {@code @wizaction} annotation.
*/
public boolean isWizaction() {
return (info == null) ? false : info.wizaction;
}
void setWizaction(boolean wizaction) {
lazyInitInfo();
info.wizaction = wizaction;
}
/**
* Returns whether JSDoc is annotated with {@code @disposes} annotation.
*/
public boolean isDisposes() {
return (info == null) ? false : info.disposedParameters != null;
}
boolean setDisposedParameter(String parameterName) {
lazyInitInfo();
// Lazily initialize disposedParameters
if (info.disposedParameters == null) {
info.disposedParameters = Sets.newHashSet();
}
if (info.disposedParameters.contains(parameterName)) {
return false;
} else {
info.disposedParameters.add(parameterName);
return true;
}
}
/**
* Return whether the function disposes of specified parameter.
*/
public boolean disposesOf(String parameterName) {
return isDisposes() && info.disposedParameters.contains(parameterName);
}
/**
* Gets the description specified by the {@code @license} annotation.
*/
public String getLicense() {
return (info == null) ? null : info.license;
}
/** License directives can appear in multiple comments, and always
* apply to the entire file. Break protection and allow outsiders to
* update the license string so that we can attach the license text even
* when the JSDocInfo has been created and tagged with other information.
* @param license String containing new license text.
*/
public void setLicense(String license) {
lazyInitInfo();
info.license = license;
}
@Override
public String toString() {
return "JSDocInfo";
}
/**
* Returns whether this {@link JSDocInfo} contains a type for {@code @extends}
* annotation.
*/
public boolean hasBaseType() {
return getBaseType() != null;
}
/**
* Adds an implemented interface. Returns whether the interface was added. If
* the interface was already present in the list, it won't get added again.
*/
boolean addImplementedInterface(JSTypeExpression interfaceName) {
lazyInitInfo();
if (info.implementedInterfaces == null) {
info.implementedInterfaces = Lists.newArrayListWithCapacity(2);
}
if (info.implementedInterfaces.contains(interfaceName)) {
return false;
}
info.implementedInterfaces.add(interfaceName);
return true;
}
/**
* Returns the types specified by the {@code @implements} annotation.
*
* @return An immutable
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> list of JSTypeExpression objects that can
* be resolved to types.
*/
public List<JSTypeExpression> getImplementedInterfaces() {
if (info == null || info.implementedInterfaces == null) {
return ImmutableList.of();
}
return Collections.unmodifiableList(info.implementedInterfaces);
}
/**
* Gets the number of interfaces specified by the {@code @implements}
* annotation.
*/
public int getImplementedInterfaceCount() {
if (info == null || info.implementedInterfaces == null) {
return 0;
}
return info.implementedInterfaces.size();
}
/**
* Adds an extended interface (for interface only).
* Returns whether the type was added.
* if the type was already present in the list, it won't get added again.
*/
boolean addExtendedInterface(JSTypeExpression type) {
lazyInitInfo();
if (info.extendedInterfaces == null) {
info.extendedInterfaces = Lists.newArrayListWithCapacity(2);
}
if (info.extendedInterfaces.contains(type)) {
return false;
}
info.extendedInterfaces.add(type);
return true;
}
/**
* Returns the interfaces extended by an interface
*
* @return An immutable list of JSTypeExpression objects that can
* be resolved to types.
*/
public List<JSTypeExpression> getExtendedInterfaces() {
if (info == null || info.extendedInterfaces == null) {
return ImmutableList.of();
}
return Collections.unmodifiableList(info.extendedInterfaces);
}
/**
* Gets the number of extended interfaces specified
*/
public int getExtendedInterfacesCount() {
if (info == null || info.extendedInterfaces == null) {
return 0;
}
return info.extendedInterfaces.size();
}
/**
* Returns the deprecation reason or null if none specified.
*/
public String getDeprecationReason() {
return info == null ? null : info.deprecated;
}
/**
* Returns the set of suppressed warnings.
*/
public Set<String> getSuppressions() {
Set<String> suppressions = info == null ? null : info.suppressions;
return suppressions == null ? Collections.<String>emptySet() : suppressions;
}
/**
* Returns the set of sideeffect notations.
*/
public Set<String> getModifies() {
Set<String> modifies = info == null ? null : info.modifies;
return modifies == null ? Collections.<String>emptySet() : modifies;
}
/**
* Returns whether a description exists for the parameter with the specified
* name.
*/
public boolean hasDescriptionForParameter(String name) {
if (documentation == null || documentation.parameters == null) {
return false;
}
return documentation.parameters.containsKey(name);
}
/**
* Returns the description for the parameter with the given name, if its
* exists.
*/
public String getDescriptionForParameter(String name) {
if (documentation == null || documentation.parameters == null) {
return null;
}
return documentation.parameters.get(name);
}
/**
* Returns the list of authors or null if none.
*/
public Collection<String> getAuthors() {
return documentation == null ?
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>TYPE;
protected ObjectType DATE_TYPE;
protected JSType ERROR_FUNCTION_TYPE;
protected ObjectType ERROR_TYPE;
protected JSType EVAL_ERROR_FUNCTION_TYPE;
protected ObjectType EVAL_ERROR_TYPE;
protected FunctionType FUNCTION_FUNCTION_TYPE;
protected FunctionType FUNCTION_INSTANCE_TYPE;
protected ObjectType FUNCTION_PROTOTYPE;
protected JSType GREATEST_FUNCTION_TYPE;
protected JSType LEAST_FUNCTION_TYPE;
protected JSType MATH_TYPE;
protected JSType NULL_TYPE;
protected JSType NUMBER_OBJECT_FUNCTION_TYPE;
protected ObjectType NUMBER_OBJECT_TYPE;
protected JSType NUMBER_STRING_BOOLEAN;
protected JSType NUMBER_TYPE;
protected FunctionType OBJECT_FUNCTION_TYPE;
protected JSType NULL_VOID;
protected JSType OBJECT_NUMBER_STRING;
protected JSType OBJECT_NUMBER_STRING_BOOLEAN;
protected JSType OBJECT_PROTOTYPE;
protected ObjectType OBJECT_TYPE;
protected JSType RANGE_ERROR_FUNCTION_TYPE;
protected ObjectType RANGE_ERROR_TYPE;
protected JSType REFERENCE_ERROR_FUNCTION_TYPE;
protected ObjectType REFERENCE_ERROR_TYPE;
protected JSType REGEXP_FUNCTION_TYPE;
protected ObjectType REGEXP_TYPE;
protected JSType STRING_OBJECT_FUNCTION_TYPE;
protected ObjectType STRING_OBJECT_TYPE;
protected JSType STRING_TYPE;
protected JSType SYNTAX_ERROR_FUNCTION_TYPE;
protected ObjectType SYNTAX_ERROR_TYPE;
protected JSType TYPE_ERROR_FUNCTION_TYPE;
protected ObjectType TYPE_ERROR_TYPE;
protected FunctionType U2U_CONSTRUCTOR_TYPE;
protected FunctionType U2U_FUNCTION_TYPE;
protected ObjectType UNKNOWN_TYPE;
protected JSType URI_ERROR_FUNCTION_TYPE;
protected ObjectType URI_ERROR_TYPE;
protected JSType VOID_TYPE;
protected int NATIVE_PROPERTIES_COUNT;
@Override
protected void setUp() throws Exception {
super.setUp();
errorReporter = new TestErrorReporter(null, null);
registry = new JSTypeRegistry(errorReporter);
initTypes();
}
protected void initTypes() {
ALL_TYPE =
registry.getNativeType(JSTypeNative.ALL_TYPE);
NO_OBJECT_TYPE =
registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE);
NO_TYPE =
registry.getNativeObjectType(JSTypeNative.NO_TYPE);
NO_RESOLVED_TYPE =
registry.getNativeObjectType(JSTypeNative.NO_RESOLVED_TYPE);
ARRAY_FUNCTION_TYPE =
registry.getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE);
ARRAY_TYPE =
registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE);
BOOLEAN_OBJECT_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE);
BOOLEAN_OBJECT_TYPE =
registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE);
BOOLEAN_TYPE =
registry.getNativeType(JSTypeNative.BOOLEAN_TYPE);
CHECKED_UNKNOWN_TYPE =
registry.getNativeObjectType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
DATE_FUNCTION_TYPE =
registry.getNativeType(JSTypeNative.DATE
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>" +
"/** @constructor \n * @param {*} var_args */ " +
"function Function(var_args) {}" +
"/** @type {!Function} */ Function.prototype.apply;" +
"/** @type {!Function} */ Function.prototype.bind;" +
"/** @type {!Function} */ Function.prototype.call;" +
"/** @constructor \n * @param {*=} arg \n @return {string} */" +
"function String(arg) {}" +
"/** @param {number} sliceArg */\n" +
"String.prototype.slice = function(sliceArg) {};" +
"/** @type {number} */ String.prototype.length;" +
"/** @constructor \n * @param {*} var_args \n @return {!Array} */" +
"function Array(var_args) {}\n" +
"/** @type {number} */ Array.prototype.length;\n" +
"/**\n" +
" * @param {...T} var_args\n" +
" * @return {number} The new length of the array.\n" +
" * @this {{length: number}|Array.<T>}\n" +
" * @template T\n" +
" * @modifies {this}\n" +
" */\n" +
"Array.prototype.push = function(var_args) {};" +
"/** @constructor */\n" +
"function Arguments() {}\n" +
"/** @type {number} */\n" +
"Arguments.prototype.length;\n" +
"/** @type {!Arguments} */\n" +
"var arguments;" +
"" + ACTIVE_X_OBJECT_DEF;
protected Compiler compiler;
protected CompilerOptions getOptions() {
CompilerOptions options = new CompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT5);
options.setWarningLevel(
DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING);
options.setWarningLevel(
DiagnosticGroups.MISPLACED_TYPE_ANNOTATION, CheckLevel.WARNING);
options.setWarningLevel(
DiagnosticGroups.INVALID_CASTS, CheckLevel.WARNING);
options.setCodingConvention(getCodingConvention());
return options;
}
protected CodingConvention getCodingConvention() {
return new GoogleCodingConvention();
}
@Override
protected void setUp() throws Exception {
compiler = new Compiler();
compiler.initOptions(getOptions());
registry = compiler.getTypeRegistry();
initTypes();
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>).
*
* When building scope information, also declares relevant information
* about types in the type registry.
*
* @author nicksantos@google.com (Nick Santos)
*/
final class TypedScopeCreator implements ScopeCreator {
/**
* A suffix for naming delegate proxies differently from their base.
*/
static final String DELEGATE_PROXY_SUFFIX =
ObjectType.createDelegateSuffix("Proxy");
static final DiagnosticType MALFORMED_TYPEDEF =
DiagnosticType.warning(
"JSC_MALFORMED_TYPEDEF",
"Typedef for {0} does not have any type information");
static final DiagnosticType ENUM_INITIALIZER =
DiagnosticType.warning(
"JSC_ENUM_INITIALIZER_NOT_ENUM",
"enum initializer must be an object literal or an enum");
static final DiagnosticType CTOR_INITIALIZER =
DiagnosticType.warning(
"JSC_CTOR_INITIALIZER_NOT_CTOR",
"Constructor {0} must be initialized at declaration");
static final DiagnosticType IFACE_INITIALIZER =
DiagnosticType.warning(
"JSC_IFACE_INITIALIZER_NOT_IFACE",
"Interface {0} must be initialized at declaration");
static final DiagnosticType CONSTRUCTOR_EXPECTED =
DiagnosticType.warning(
"JSC_REFLECT_CONSTRUCTOR_EXPECTED",
"Constructor expected as first argument");
static final DiagnosticType UNKNOWN_LENDS =
DiagnosticType.warning(
"JSC_UNKNOWN_LENDS",
"Variable {0} not declared before @lends annotation.");
static final DiagnosticType LENDS_ON_NON_OBJECT =
DiagnosticType.warning(
"JSC_LENDS_ON_NON_OBJECT",
"May only lend properties to object types. {0} has type {1}.");
private final AbstractCompiler compiler;
private final ErrorReporter typeParsingErrorReporter;
private final TypeValidator validator;
private final CodingConvention codingConvention;
private final JSTypeRegistry typeRegistry;
private final List<ObjectType> delegateProxyPrototypes = Lists.newArrayList();
private final Map<String, String> delegateCallingConventions =
Maps.newHashMap();
// Simple properties inferred about functions.
private final Map<Node, AstFunctionContents> functionAnalysisResults =
Maps.newHashMap();
// For convenience
private final ObjectType unknownType;
/**
* Defer attachment of types to nodes until all type names
* have been resolved. Then, we can resolve the type and attach it.
*/
private class DeferredSetType {
final Node node;
final JSType type;
DeferredSetType(Node node, JSType type) {
Preconditions.checkNotNull(node);
Preconditions.checkNotNull(type);
this.node = node;
this.type = type;
// Other parts of this pass may read off the node.
// (like when we set the LHS of an assign with a typed RHS function.)
node.setJSType(type);
}
void resolve(Scope scope) {
node.setJSType(type.resolve(typeParsingErrorReporter, scope));
}
}
TypedScopeCreator(AbstractCompiler compiler) {
this(compiler, compiler.getCodingConvention());
}
TypedScopeCreator(AbstractCompiler compiler,
CodingConvention codingConvention) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> this.compiler = compiler;
this.validator = compiler.getTypeValidator();
this.codingConvention = codingConvention;
this.typeRegistry = compiler.getTypeRegistry();
this.typeParsingErrorReporter = typeRegistry.getErrorReporter();
this.unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE);
}
/**
* Creates a scope with all types declared. Declares newly discovered types
* and type properties in the type registry.
*/
@Override
public Scope createScope(Node root, Scope parent) {
// Constructing the global scope is very different than constructing
// inner scopes, because only global scopes can contain named classes that
// show up in the type registry.
Scope newScope = null;
AbstractScopeBuilder scopeBuilder = null;
if (parent == null) {
JSType globalThis =
typeRegistry.getNativeObjectType(JSTypeNative.GLOBAL_THIS);
// Mark the main root, the externs root, and the src root
// with the global this type.
root.setJSType(globalThis);
root.getFirstChild().setJSType(globalThis);
root.getLastChild().setJSType(globalThis);
// Run a first-order analysis over the syntax tree.
(new FirstOrderFunctionAnalyzer(compiler, functionAnalysisResults))
.process(root.getFirstChild(), root.getLastChild());
// Find all the classes in the global scope.
newScope = createInitialScope(root);
GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope);
scopeBuilder = globalScopeBuilder;
NodeTraversal.traverse(compiler, root, scopeBuilder);
} else {
newScope = new Scope(parent, root);
LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope);
scopeBuilder = localScopeBuilder;
localScopeBuilder.build();
}
scopeBuilder.resolveStubDeclarations();
// Gather the properties in each function that we found in the
// global scope, if that function has a @this type that we can
// build properties on.
for (Node functionNode : scopeBuilder.nonExternFunctions) {
JSType type = functionNode.getJSType();
if (type != null && type.isFunctionType()) {
FunctionType fnType = type.toMaybeFunctionType();
JSType fnThisType = fnType.getTypeOfThis();
if (!fnThisType.isUnknownType()) {
NodeTraversal.traverse(compiler, functionNode.getLastChild(),
scopeBuilder.new CollectProperties(fnThisType));
}
}
}
if (parent == null) {
codingConvention.defineDelegateProxyPrototypeProperties(
typeRegistry, newScope, delegateProxyPrototypes,
delegateCallingConventions);
}
newScope.setTypeResolver(scopeBuilder);
return newScope;
}
/**
* Patches a given global scope by removing variables previously declared in
* a script and re-traversing a new version of that script.
*
* @param globalScope The global scope generated by {@code createScope}.
* @param scriptRoot The script that is modified.
*/
void patchGlobalScope(Scope globalScope, Node scriptRoot) {
// Preconditions: This is supposed to be called only on (named) SCRIPT nodes
// and a global typed scope should have been generated already.
Preconditions.check
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>FunctionType(tId);
declareNativeType(scope, t.getInstanceType().getReferenceName(), t);
declareNativeType(
scope, t.getPrototype().getReferenceName(), t.getPrototype());
}
private void declareNativeValueType(Scope scope, String name,
JSTypeNative tId) {
declareNativeType(scope, name, typeRegistry.getNativeType(tId));
}
private static void declareNativeType(Scope scope, String name, JSType t) {
scope.declare(name, null, t, null, false);
}
private static class DiscoverEnumsAndTypedefs
extends AbstractShallowStatementCallback {
private final JSTypeRegistry registry;
DiscoverEnumsAndTypedefs(JSTypeRegistry registry) {
this.registry = registry;
}
@Override
public void visit(NodeTraversal t, Node node, Node parent) {
switch (node.getType()) {
case Token.VAR:
for (Node child = node.getFirstChild();
child != null; child = child.getNext()) {
identifyNameNode(
child, NodeUtil.getBestJSDocInfo(child));
}
break;
case Token.EXPR_RESULT:
Node firstChild = node.getFirstChild();
if (firstChild.isAssign()) {
identifyNameNode(
firstChild.getFirstChild(), firstChild.getJSDocInfo());
} else {
identifyNameNode(
firstChild, firstChild.getJSDocInfo());
}
break;
}
}
private void identifyNameNode(
Node nameNode, JSDocInfo info) {
if (nameNode.isQualifiedName()) {
if (info != null) {
if (info.hasEnumParameterType()) {
registry.identifyNonNullableName(nameNode.getQualifiedName());
} else if (info.hasTypedefType()) {
registry.identifyNonNullableName(nameNode.getQualifiedName());
}
}
}
}
}
private JSType getNativeType(JSTypeNative nativeType) {
return typeRegistry.getNativeType(nativeType);
}
private abstract class AbstractScopeBuilder
implements NodeTraversal.Callback, Scope.TypeResolver {
/**
* The scope that we're building.
*/
final Scope scope;
private final List<DeferredSetType> deferredSetTypes =
Lists.newArrayList();
/**
* Functions that we found in the global scope and not in externs.
*/
private final List<Node> nonExternFunctions = Lists.newArrayList();
/**
* Object literals with a @lends annotation aren't analyzed until we
* reach the root of the statement they're defined in.
*
* This ensures that if there are any @lends annotations on the object
* literals, the type on the @lends annotation resolves correctly.
*
* For more information, see
* http://code.google.com/p/closure-compiler/issues/detail?id=314
*/
private List<Node> lentObjectLiterals = null;
/**
* Type-less stubs.
*
* If at the end of traversal, we still don't have types for these
* stubs, then we should declare UNKNOWN types.
*/
private final List<StubDeclaration> stubDeclarations =
Lists.newArrayList();
/**
* The current
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> source file that we're in.
*/
private String sourceName = null;
/**
* The InputId of the current node.
*/
private InputId inputId;
private AbstractScopeBuilder(Scope scope) {
this.scope = scope;
}
void setDeferredType(Node node, JSType type) {
deferredSetTypes.add(new DeferredSetType(node, type));
}
@Override
public void resolveTypes() {
// Resolve types and attach them to nodes.
for (DeferredSetType deferred : deferredSetTypes) {
deferred.resolve(scope);
}
// Resolve types and attach them to scope slots.
Iterator<Var> vars = scope.getVars();
while (vars.hasNext()) {
vars.next().resolveType(typeParsingErrorReporter);
}
// Tell the type registry that any remaining types
// are unknown.
typeRegistry.resolveTypesInScope(scope);
}
@Override
public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
inputId = t.getInputId();
if (n.isFunction() ||
n.isScript()) {
Preconditions.checkNotNull(inputId);
sourceName = NodeUtil.getSourceName(n);
}
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
boolean descend = parent == null || !parent.isFunction() ||
n == parent.getFirstChild() || parent == scope.getRootNode();
if (descend) {
// Handle hoisted functions on pre-order traversal, so that they
// get hit before other things in the scope.
if (NodeUtil.isStatementParent(n)) {
for (Node child = n.getFirstChild();
child != null;
child = child.getNext()) {
if (NodeUtil.isHoistedFunctionDeclaration(child)) {
defineFunctionLiteral(child);
}
}
}
}
return descend;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
inputId = t.getInputId();
attachLiteralTypes(n);
switch (n.getType()) {
case Token.CALL:
checkForClassDefiningCalls(t, n);
checkForCallingConventionDefiningCalls(n, delegateCallingConventions);
break;
case Token.FUNCTION:
if (t.getInput() == null || !t.getInput().isExtern()) {
nonExternFunctions.add(n);
}
// Hoisted functions are handled during pre-traversal.
if (!NodeUtil.isHoistedFunctionDeclaration(n)) {
defineFunctionLiteral(n);
}
break;
case Token.ASSIGN:
// Handle initialization of properties.
Node firstChild = n.getFirstChild();
if (firstChild.isGetProp() &&
firstChild.isQualifiedName()) {
maybeDeclareQualifiedName(t, n.getJSDocInfo(),
firstChild, n, firstChild.getNext());
}
break;
case Token.CATCH:
defineCatch(n);
break;
case Token.VAR:
defineVar(n);
break;
case Token.GETPROP:
// Handle stubbed properties.
if (parent.isExprResult() &&
n.isQualifiedName())
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Registry.createEnumType(name, rValue, elementsType);
if (rValue != null && rValue.isObjectLit()) {
// collect enum elements
Node key = rValue.getFirstChild();
while (key != null) {
String keyName = NodeUtil.getStringValue(key);
if (keyName == null) {
// GET and SET don't have a String value;
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else if (!codingConvention.isValidEnumKey(keyName)) {
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else {
enumType.defineElement(keyName, key);
}
key = key.getNext();
}
}
}
if (name != null && scope.isGlobal()) {
typeRegistry.declareType(name, enumType.getElementsType());
}
return enumType;
}
/**
* Defines a typed variable. The defining node will be annotated with the
* variable's type or {@code null} if its type is inferred.
* @param name the defining node. It must be a {@link Token#NAME}.
* @param parent the {@code name}'s parent.
* @param type the variable's type. It may be {@code null}, in which case
* the variable's type will be inferred.
*/
private void defineSlot(Node name, Node parent, JSType type) {
defineSlot(name, parent, type, type == null);
}
/**
* Defines a typed variable. The defining node will be annotated with the
* variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is
* inferred.
*
* Slots may be any variable or any qualified name in the global scope.
*
* @param n the defining NAME or GETPROP node.
* @param parent the {@code n}'s parent.
* @param type the variable's type. It may be {@code null} if
* {@code inferred} is {@code true}.
*/
void defineSlot(Node n, Node parent, JSType type, boolean inferred) {
Preconditions.checkArgument(inferred || type != null);
// Only allow declarations of NAMEs and qualified names.
// Object literal keys will have to compute their names themselves.
if (n.isName()) {
Preconditions.checkArgument(
parent.isFunction() ||
parent.isVar() ||
parent.isParamList() ||
parent.isCatch());
} else {
Preconditions.checkArgument(
n.isGetProp() &&
(parent.isAssign() ||
parent.isExprResult()));
}
defineSlot(n, parent, n.getQualifiedName(), type, inferred);
}
/**
* Defines a symbol in the current scope.
*
* @param n the defining NAME or GETPROP or object literal key node.
* @param parent the {@code n}'s parent.
* @param variableName The name that this should be known by.
* @param type the variable's type. It may be {@code null} if
* {@code inferred} is {@code true}.
* @param inferred Whether the type is inferred or declared.
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> or not
* is really really obnoxious.
*
* The problem is that there are two (equally valid) coding styles:
*
* (function() {
* /* The authoritative definition of goog.bar. /
* goog.bar = function() {};
* })();
*
* function f() {
* goog.bar();
* /* Reset goog.bar to a no-op. /
* goog.bar = function() {};
* }
*
* In a dynamic language with first-class functions, it's very difficult
* to know which one the user intended without looking at lots of
* contextual information (the second example demonstrates a small case
* of this, but there are some really pathological cases as well).
*
* The current algorithm checks if either the declaration has
* JsDoc type information, or @const with a known type,
* or a function literal with a name we haven't seen before.
*/
private boolean isQualifiedNameInferred(
String qName, Node n, JSDocInfo info,
Node rhsValue, JSType valueType) {
if (valueType == null) {
return true;
}
// Prototypes of constructors and interfaces are always declared.
if (qName != null && qName.endsWith(".prototype")) {
String className = qName.substring(0, qName.lastIndexOf(".prototype"));
Var slot = scope.getSlot(className);
JSType classType = slot == null ? null : slot.getType();
if (classType != null
&& (classType.isConstructor() || classType.isInterface())) {
return false;
}
}
boolean inferred = true;
if (info != null) {
inferred = !(info.hasType()
|| info.hasEnumParameterType()
|| (isConstantSymbol(info, n) && valueType != null
&& !valueType.isUnknownType())
|| FunctionTypeBuilder.isFunctionTypeDeclaration(info));
}
if (inferred && rhsValue != null && rhsValue.isFunction()) {
if (info != null) {
return false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
// Check if this is in a conditional block.
// Functions assigned in conditional blocks are inferred.
for (Node current = n.getParent();
!(current.isScript() || current.isFunction());
current = current.getParent()) {
if (NodeUtil.isControlStructure(current)) {
return true;
}
}
// Check if this is assigned in an inner scope.
// Functions assigned in inner scopes are inferred.
AstFunctionContents contents =
getFunctionAnalysisResults(scope.getRootNode());
if (contents == null ||
!contents.getEscapedQualifiedNames().contains(qName)) {
return false;
}
}
}
return inferred;
}
private boolean isConstantSymbol(JSDocInfo info, Node node) {
if (info != null && info.isConstant()) {
return true;
}
switch (node.getType()) {
case Token.NAME:
return NodeUtil.isConstantByConvention(
compiler.getCodingConvention(), node, node.getParent());
case Token.GETPROP:
return node.isQualifiedName
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>() && NodeUtil.isConstantByConvention(
compiler.getCodingConvention(), node.getLastChild(), node);
}
return false;
}
/**
* Find the ObjectType associated with the given slot.
* @param slotName The name of the slot to find the type in.
* @return An object type, or null if this slot does not contain an object.
*/
private ObjectType getObjectSlot(String slotName) {
Var ownerVar = scope.getVar(slotName);
if (ownerVar != null) {
JSType ownerVarType = ownerVar.getType();
return ObjectType.cast(ownerVarType == null ?
null : ownerVarType.restrictByNotNullOrUndefined());
}
return null;
}
/**
* Resolve any stub declarations to unknown types if we could not
* find types for them during traversal.
*/
void resolveStubDeclarations() {
for (StubDeclaration stub : stubDeclarations) {
Node n = stub.node;
Node parent = n.getParent();
String qName = n.getQualifiedName();
String propName = n.getLastChild().getString();
String ownerName = stub.ownerName;
boolean isExtern = stub.isExtern;
if (scope.isDeclared(qName, false)) {
continue;
}
// If we see a stub property, make sure to register this property
// in the type registry.
ObjectType ownerType = getObjectSlot(ownerName);
defineSlot(n, parent, unknownType, true);
if (ownerType != null &&
(isExtern || ownerType.isFunctionPrototypeType())) {
// If this is a stub for a prototype, just declare it
// as an unknown type. These are seen often in externs.
ownerType.defineInferredProperty(
propName, unknownType, n);
} else {
typeRegistry.registerPropertyOnType(
propName, ownerType == null ? unknownType : ownerType);
}
}
}
/**
* Collects all declared properties in a function, and
* resolves them relative to the global scope.
*/
private final class CollectProperties
extends AbstractShallowStatementCallback {
private final JSType thisType;
CollectProperties(JSType thisType) {
this.thisType = thisType;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isExprResult()) {
Node child = n.getFirstChild();
switch (child.getType()) {
case Token.ASSIGN:
maybeCollectMember(child.getFirstChild(), child,
child.getLastChild());
break;
case Token.GETPROP:
maybeCollectMember(child, child, null);
break;
}
}
}
private void maybeCollectMember(Node member,
Node nodeWithJsDocInfo, @Nullable Node value) {
JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo();
// Do nothing if there is no JSDoc type info, or
// if the node is not a member expression, or
// if the member expression is not of the form: this.someProperty.
if (info == null ||
!member.isGetProp() ||
!member.getFirstChild().isThis()) {
return;
}
member.getFirstChild().set
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>JSType(thisType);
// TODO(johnlenz): We are evaluating these values in the wrong scope:
// https://code.google.com/p/closure-compiler/issues/detail?id=926
JSType thisObjectType = thisType.toObjectType();
if (thisObjectType != null) {
ImmutableList<TemplateType> keys =
thisObjectType.getTemplateTypeMap().getTemplateKeys();
typeRegistry.setTemplateTypeNames(keys);
}
JSType jsType = getDeclaredType(info, member, value);
if (thisObjectType != null) {
typeRegistry.clearTemplateTypeNames();
}
Node name = member.getLastChild();
if (jsType != null &&
(name.isName() || name.isString()) &&
thisType.toObjectType() != null) {
thisType.toObjectType().defineDeclaredProperty(
name.getString(),
jsType,
member);
}
}
} // end CollectProperties
}
/**
* A stub declaration without any type information.
*/
private static final class StubDeclaration {
private final Node node;
private final boolean isExtern;
private final String ownerName;
private StubDeclaration(Node node, boolean isExtern, String ownerName) {
this.node = node;
this.isExtern = isExtern;
this.ownerName = ownerName;
}
}
/**
* A shallow traversal of the global scope to build up all classes,
* functions, and methods.
*/
private final class GlobalScopeBuilder extends AbstractScopeBuilder {
private GlobalScopeBuilder(Scope scope) {
super(scope);
}
/**
* Visit a node in the global scope, and add anything it declares to the
* global symbol table.
*
* @param t The current traversal.
* @param n The node being visited.
* @param parent The parent of n
*/
@Override public void visit(NodeTraversal t, Node n, Node parent) {
super.visit(t, n, parent);
switch (n.getType()) {
case Token.VAR:
// Handle typedefs.
if (n.hasOneChild()) {
checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo());
}
break;
}
}
@Override
void maybeDeclareQualifiedName(
NodeTraversal t, JSDocInfo info,
Node n, Node parent, Node rhsValue) {
checkForTypedef(t, n, info);
super.maybeDeclareQualifiedName(t, info, n, parent, rhsValue);
}
/**
* Handle typedefs.
* @param t The current traversal.
* @param candidate A qualified name node.
* @param info JSDoc comments.
*/
private void checkForTypedef(
NodeTraversal t, Node candidate, JSDocInfo info) {
if (info == null || !info.hasTypedefType()) {
return;
}
String typedef = candidate.getQualifiedName();
if (typedef == null) {
return;
}
// TODO(nicksantos|user): This is a terrible, terrible hack
// to bail out on recursive typedefs. We'll eventually need
// to handle these properly.
typeRegistry.declareType(typedef, unknownType);
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> JSType realType = info.getTypedefType().evaluate(scope, typeRegistry);
if (realType == null) {
compiler.report(
JSError.make(
t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef));
}
typeRegistry.overwriteDeclaredType(typedef, realType);
if (candidate.isGetProp()) {
defineSlot(candidate, candidate.getParent(),
getNativeType(NO_TYPE), false);
}
}
} // end GlobalScopeBuilder
/**
* A shallow traversal of a local scope to find all arguments and
* local variables.
*/
private final class LocalScopeBuilder extends AbstractScopeBuilder {
/**
* @param scope The scope that we're building.
*/
private LocalScopeBuilder(Scope scope) {
super(scope);
}
/**
* Traverse the scope root and build it.
*/
void build() {
NodeTraversal.traverse(compiler, scope.getRootNode(), this);
AstFunctionContents contents =
getFunctionAnalysisResults(scope.getRootNode());
if (contents != null) {
for (String varName : contents.getEscapedVarNames()) {
Var v = scope.getVar(varName);
Preconditions.checkState(v.getScope() == scope);
v.markEscaped();
}
for (Multiset.Entry<String> entry :
contents.getAssignedNameCounts().entrySet()) {
Var v = scope.getVar(entry.getElement());
Preconditions.checkState(v.getScope() == scope);
if (entry.getCount() == 1) {
v.markAssignedExactlyOnce();
}
}
}
}
/**
* Visit a node in a local scope, and add any local variables or catch
* parameters into the local symbol table.
*
* @param t The node traversal.
* @param n The node being visited.
* @param parent The parent of n
*/
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (n == scope.getRootNode()) {
return;
}
if (n.isParamList() && parent == scope.getRootNode()) {
handleFunctionInputs(parent);
return;
}
super.visit(t, n, parent);
}
/** Handle bleeding functions and function parameters. */
private void handleFunctionInputs(Node fnNode) {
// Handle bleeding functions.
Node fnNameNode = fnNode.getFirstChild();
String fnName = fnNameNode.getString();
if (!fnName.isEmpty()) {
Scope.Var fnVar = scope.getVar(fnName);
if (fnVar == null ||
// Make sure we're not touching a native function. Native
// functions aren't bleeding, but may not have a declaration
// node.
(fnVar.getNameNode() != null &&
// Make sure that the function is actually bleeding by checking
// if has already been declared.
fnVar.getInitialValue() != fnNode)) {
defineSlot(fnNameNode, fnNode, fnNode.getJSType(), false);
}
}
declareArguments(fnNode);
}
/**
* Declares all of a function's arguments.
*/
private void declareArguments(Node functionNode) {
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
Node astParameters = functionNode.getFirstChild().getNext();
Node iifeArgumentNode = null;
if (NodeUtil.isCallOrNewTarget(functionNode)) {
iifeArgumentNode = functionNode.getNext();
}
FunctionType functionType =
JSType.toMaybeFunctionType(functionNode.getJSType());
if (functionType != null) {
Node jsDocParameters = functionType.getParametersNode();
if (jsDocParameters != null) {
Node jsDocParameter = jsDocParameters.getFirstChild();
for (Node astParameter : astParameters.children()) {
JSType paramType = jsDocParameter == null ?
unknownType : jsDocParameter.getJSType();
boolean inferred = paramType == null || paramType == unknownType;
if (iifeArgumentNode != null && inferred) {
String argumentName = iifeArgumentNode.getQualifiedName();
Var argumentVar =
argumentName == null || scope.getParent() == null
? null : scope.getParent().getVar(argumentName);
if (argumentVar != null && !argumentVar.isTypeInferred()) {
paramType = argumentVar.getType();
}
}
if (paramType == null) {
paramType = unknownType;
}
defineSlot(astParameter, functionNode, paramType, inferred);
if (jsDocParameter != null) {
jsDocParameter = jsDocParameter.getNext();
}
if (iifeArgumentNode != null) {
iifeArgumentNode = iifeArgumentNode.getNext();
}
}
}
}
} // end declareArguments
} // end LocalScopeBuilder
/**
* Does a first-order function analysis that just looks at simple things
* like what variables are escaped, and whether 'this' is used.
*/
private static class FirstOrderFunctionAnalyzer
extends AbstractScopedCallback implements CompilerPass {
private final AbstractCompiler compiler;
private final Map<Node, AstFunctionContents> data;
FirstOrderFunctionAnalyzer(
AbstractCompiler compiler, Map<Node, AstFunctionContents> outParam) {
this.compiler = compiler;
this.data = outParam;
}
@Override public void process(Node externs, Node root) {
if (externs == null) {
NodeTraversal.traverse(compiler, root, this);
} else {
NodeTraversal.traverseRoots(
compiler, ImmutableList.of(externs, root), this);
}
}
@Override public void enterScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
data.put(n, new AstFunctionContents(n));
}
}
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (t.inGlobalScope()) {
return;
}
if (n.isReturn() && n.getFirstChild() != null) {
data.get(t.getScopeRoot()).recordNonEmptyReturn();
}
if (t.getScopeDepth() <= 1) {
// The first-order function analyzer looks at two types of variables:
//
// 1) Local variables that are assigned in inner scopes ("escaped vars")
//
// 2) Local variables that are assigned more than once.
//
// We treat all global variables as escaped
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> an exception if no pass with the given name exists.
*/
private static int findPassIndexByName(
List<PassFactory> factoryList, String name) {
for (int i = 0; i < factoryList.size(); i++) {
if (factoryList.get(i).getName().equals(name)) {
return i;
}
}
throw new IllegalArgumentException(
"No factory named '" + name + "' in the factory list");
}
/**
* Find the first pass provider that does not have a delegate.
*/
final PassConfig getBasePassConfig() {
PassConfig current = this;
while (current instanceof PassConfigDelegate) {
current = ((PassConfigDelegate) current).delegate;
}
return current;
}
/**
* Get intermediate state for a running pass config, so it can
* be paused and started again later.
*/
protected abstract State getIntermediateState();
/**
* Set the intermediate state for a pass config, to restart
* a compilation process that had been previously paused.
*/
protected abstract void setIntermediateState(State state);
/**
* An implementation of PassConfig that just proxies all its method calls
* into an inner class.
*/
static class PassConfigDelegate extends PassConfig {
private final PassConfig delegate;
PassConfigDelegate(PassConfig delegate) {
super(delegate.options);
this.delegate = delegate;
}
@Override protected List<PassFactory> getChecks() {
return delegate.getChecks();
}
@Override protected List<PassFactory> getOptimizations() {
return delegate.getOptimizations();
}
@Override MemoizedScopeCreator getTypedScopeCreator() {
return delegate.getTypedScopeCreator();
}
@Override Scope getTopScope() {
return delegate.getTopScope();
}
@Override protected State getIntermediateState() {
return delegate.getIntermediateState();
}
@Override protected void setIntermediateState(State state) {
delegate.setIntermediateState(state);
}
}
/**
* Intermediate state for a running pass configuration.
*/
public static class State implements Serializable {
private static final long serialVersionUID = 1L;
final Map<String, Integer> cssNames;
final Set<String> exportedNames;
final CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator;
final VariableMap variableMap;
final VariableMap propertyMap;
final VariableMap anonymousFunctionNameMap;
final VariableMap stringMap;
final FunctionNames functionNames;
final String idGeneratorMap;
public State(Map<String, Integer> cssNames, Set<String> exportedNames,
CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator,
VariableMap variableMap, VariableMap propertyMap,
VariableMap anonymousFunctionNameMap,
VariableMap stringMap, FunctionNames functionNames,
String idGeneratorMap) {
this.cssNames = cssNames;
this.exportedNames = exportedNames;
this.crossModuleIdGenerator = crossModuleIdGenerator;
this.variableMap = variableMap;
this.propertyMap = propertyMap;
this.anonymousFunctionNameMap = anonymousFunctionNameMap;
this.stringMap = stringMap;
this.idGeneratorMap = idGeneratorMap;
this.functionNames = functionNames;
}
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Nick Santos
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import java.io.Serializable;
/**
* A property slot of an object.
* @author nicksantos@google.com (Nick Santos)
*/
public final class Property
implements Serializable, StaticSlot<JSType>, StaticReference<JSType> {
private static final long serialVersionUID = 1L;
/**
* Property's name.
*/
private final String name;
/**
* Property's type.
*/
private JSType type;
/**
* Whether the property's type is inferred.
*/
private final boolean inferred;
/**
* The node corresponding to this property, e.g., a GETPROP node that
* declares this property.
*/
private Node propertyNode;
/** The JSDocInfo for this property. */
private JSDocInfo docInfo = null;
Property(String name, JSType type, boolean inferred,
Node propertyNode) {
this.name = name;
this.type = type;
this.inferred = inferred;
this.propertyNode = propertyNode;
}
@Override
public String getName() {
return name;
}
@Override
public Node getNode() {
return propertyNode;
}
@Override
public StaticSourceFile getSourceFile() {
return propertyNode ==
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> null ? null : propertyNode.getStaticSourceFile();
}
@Override
public Property getSymbol() {
return this;
}
@Override
public Property getDeclaration() {
return propertyNode == null ? null : this;
}
@Override
public JSType getType() {
return type;
}
@Override
public boolean isTypeInferred() {
return inferred;
}
boolean isFromExterns() {
return propertyNode == null ? false : propertyNode.isFromExterns();
}
void setType(JSType type) {
this.type = type;
}
@Override public JSDocInfo getJSDocInfo() {
return this.docInfo;
}
void setJSDocInfo(JSDocInfo info) {
this.docInfo = info;
}
public void setNode(Node n) {
this.propertyNode = n;
}
}
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>, new Node(Token.PARAM_LIST), null);
this.kind = Kind.INTERFACE;
this.typeOfThis = new InstanceObjectType(registry, this);
}
/** Creates an instance for a function that is an interface. */
static FunctionType forInterface(
JSTypeRegistry registry, String name, Node source,
TemplateTypeMap typeParameters) {
return new FunctionType(registry, name, source, typeParameters);
}
@Override
public boolean isInstanceType() {
// The universal constructor is its own instance, bizarrely. It overrides
// getConstructor() appropriately when it's declared.
return this == registry.getNativeType(U2U_CONSTRUCTOR_TYPE);
}
@Override
public boolean isConstructor() {
return kind == Kind.CONSTRUCTOR;
}
@Override
public boolean isInterface() {
return kind == Kind.INTERFACE;
}
@Override
public boolean isOrdinaryFunction() {
return kind == Kind.ORDINARY;
}
/**
* When a class B inherits from A and A is annotated as a struct, then B
* automatically gets the annotation, even if B's constructor is not
* explicitly annotated.
*/
public boolean makesStructs() {
if (!isConstructor()) {
return false;
}
if (propAccess == PropAccess.STRUCT) {
return true;
}
FunctionType superc = getSuperClassConstructor();
if (superc != null && superc.makesStructs()) {
setStruct();
return true;
}
return false;
}
/**
* When a class B inherits from A and A is annotated as a dict, then B
* automatically gets the annotation, even if B's constructor is not
* explicitly annotated.
*/
public boolean makesDicts() {
if (!isConstructor()) {
return false;
}
if (propAccess == PropAccess.DICT) {
return true;
}
FunctionType superc = getSuperClassConstructor();
if (superc != null && superc.makesDicts()) {
setDict();
return true;
}
return false;
}
public void setStruct() {
propAccess = PropAccess.STRUCT;
}
public void setDict() {
propAccess = PropAccess.DICT;
}
@Override
public FunctionType toMaybeFunctionType() {
return this;
}
@Override
public boolean canBeCalled() {
return true;
}
public boolean hasImplementedInterfaces() {
if (!implementedInterfaces.isEmpty()){
return true;
}
FunctionType superCtor = isConstructor() ?
getSuperClassConstructor() : null;
if (superCtor != null) {
return superCtor.hasImplementedInterfaces();
}
return false;
}
public Iterable<Node> getParameters() {
Node n = getParametersNode();
if (n != null) {
return n.children();
} else {
return Collections.emptySet();
}
}
/** Gets an LP node that contains all params. May be null. */
public Node getParametersNode() {
return call.parameters;
}
/** Gets the minimum number of arguments that this function requires. */
public int getMinArguments() {
// NOTE(nicksantos): There are some native functions that have optional
//
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> parameters before required parameters. This algorithm finds the position
// of the last required parameter.
int i = 0;
int min = 0;
for (Node n : getParameters()) {
i++;
if (!n.isOptionalArg() && !n.isVarArgs()) {
min = i;
}
}
return min;
}
/**
* Gets the maximum number of arguments that this function requires,
* or Integer.MAX_VALUE if this is a variable argument function.
*/
public int getMaxArguments() {
Node params = getParametersNode();
if (params != null) {
Node lastParam = params.getLastChild();
if (lastParam == null || !lastParam.isVarArgs()) {
return params.getChildCount();
}
}
return Integer.MAX_VALUE;
}
public JSType getReturnType() {
return call.returnType;
}
public boolean isReturnTypeInferred() {
return call.returnTypeInferred;
}
/** Gets the internal arrow type. For use by subclasses only. */
ArrowType getInternalArrowType() {
return call;
}
@Override
public Property getSlot(String name) {
if ("prototype".equals(name)) {
// Lazy initialization of the prototype field.
getPrototype();
return prototypeSlot;
} else {
return super.getSlot(name);
}
}
/**
* Includes the prototype iff someone has created it. We do not want
* to expose the prototype for ordinary functions.
*/
@Override
public Set<String> getOwnPropertyNames() {
if (prototypeSlot == null) {
return super.getOwnPropertyNames();
} else {
Set<String> names = Sets.newHashSet("prototype");
names.addAll(super.getOwnPropertyNames());
return names;
}
}
/**
* Gets the {@code prototype} property of this function type. This is
* equivalent to {@code (ObjectType) getPropertyType("prototype")}.
*/
public ObjectType getPrototype() {
// lazy initialization of the prototype field
if (prototypeSlot == null) {
String refName = getReferenceName();
if (refName == null) {
// Someone is trying to access the prototype of a structural function.
// We don't want to give real properties to this prototype, because
// then it would propagate to all structural functions.
setPrototypeNoCheck(
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE),
null);
} else {
setPrototype(
new PrototypeObjectType(
registry,
getReferenceName() + ".prototype",
registry.getNativeObjectType(OBJECT_TYPE),
isNativeObjectType(), null),
null);
}
}
return (ObjectType) prototypeSlot.getType();
}
/**
* Sets the prototype, creating the prototype object from the given
* base type.
* @param baseType The base type.
*/
public void setPrototypeBasedOn(ObjectType baseType) {
setPrototypeBasedOn(baseType, null);
}
void setPrototypeBasedOn(ObjectType baseType, Node propertyNode) {
// This is a bit weird. We need to successfully handle these
// two cases:
// Foo.prototype = new Bar();
// and
// Foo.prototype = {baz: 3};
// In the first case
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> }
return interfaces;
}
private void addRelatedInterfaces(ObjectType instance, Set<ObjectType> set) {
FunctionType constructor = instance.getConstructor();
if (constructor != null) {
if (!constructor.isInterface()) {
return;
}
set.add(instance);
for (ObjectType interfaceType : instance.getCtorExtendedInterfaces()) {
addRelatedInterfaces(interfaceType, set);
}
}
}
/** Returns interfaces implemented directly by a class or its superclass. */
public Iterable<ObjectType> getImplementedInterfaces() {
FunctionType superCtor = isConstructor() ?
getSuperClassConstructor() : null;
if (superCtor == null) {
return implementedInterfaces;
} else {
return Iterables.concat(
implementedInterfaces, superCtor.getImplementedInterfaces());
}
}
/** Returns interfaces directly implemented by the class. */
public Iterable<ObjectType> getOwnImplementedInterfaces() {
return implementedInterfaces;
}
public void setImplementedInterfaces(List<ObjectType> implementedInterfaces) {
if (isConstructor()) {
// Records this type for each implemented interface.
for (ObjectType type : implementedInterfaces) {
registry.registerTypeImplementingInterface(this, type);
typeOfThis.extendTemplateTypeMap(type.getTemplateTypeMap());
}
this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces);
} else {
throw new UnsupportedOperationException();
}
}
/**
* Returns all extended interfaces declared by an interfaces or its super-
* interfaces. If this is called before all types are resolved, it may return
* an incomplete set.
*/
public Iterable<ObjectType> getAllExtendedInterfaces() {
// Store them in a linked hash set, so that the compile job is
// deterministic.
Set<ObjectType> extendedInterfaces = Sets.newLinkedHashSet();
for (ObjectType interfaceType : getExtendedInterfaces()) {
addRelatedExtendedInterfaces(interfaceType, extendedInterfaces);
}
return extendedInterfaces;
}
private void addRelatedExtendedInterfaces(ObjectType instance,
Set<ObjectType> set) {
FunctionType constructor = instance.getConstructor();
if (constructor != null) {
set.add(instance);
for (ObjectType interfaceType : constructor.getExtendedInterfaces()) {
addRelatedExtendedInterfaces(interfaceType, set);
}
}
}
/** Returns interfaces directly extended by an interface */
public Iterable<ObjectType> getExtendedInterfaces() {
return extendedInterfaces;
}
/** Returns the number of interfaces directly extended by an interface */
public int getExtendedInterfacesCount() {
return extendedInterfaces.size();
}
public void setExtendedInterfaces(List<ObjectType> extendedInterfaces)
throws UnsupportedOperationException {
if (isInterface()) {
this.extendedInterfaces = ImmutableList.copyOf(extendedInterfaces);
for (ObjectType extendedInterface : this.extendedInterfaces) {
typeOfThis.extendTemplateTypeMap(
extendedInterface.getTemplateTypeMap());
}
} else {
throw new UnsupportedOperationException();
}
}
@Override
public JSType getPropertyType(String name) {
if (!hasOwnProperty(name)) {
// Define the "call", "apply", and "bind" functions lazily.
boolean isCall = "call".equals(name);
boolean isBind = "bind".equals(name);
if (isCall || isBind) {
defineDeclaredProperty(name, getCallOrBindSignature(
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>Arg.isOptionalArg()
|| firstArg.isVarArgs()) {
thisTypeNode.setOptionalArg(true);
}
}
builder.withParamsNode(params);
}
return builder.build();
}
@Override
boolean defineProperty(String name, JSType type,
boolean inferred, Node propertyNode) {
if ("prototype".equals(name)) {
ObjectType objType = type.toObjectType();
if (objType != null) {
if (prototypeSlot != null &&
objType.isEquivalentTo(prototypeSlot.getType())) {
return true;
}
setPrototypeBasedOn(objType, propertyNode);
return true;
} else {
return false;
}
}
return super.defineProperty(name, type, inferred, propertyNode);
}
/**
* Computes the supremum or infimum of two functions.
* Because sup() and inf() share a lot of logic for functions, we use
* a single helper.
* @param leastSuper If true, compute the supremum of {@code this} with
* {@code that}. Otherwise, compute the infimum.
* @return The least supertype or greatest subtype.
*/
FunctionType supAndInfHelper(FunctionType that, boolean leastSuper) {
// NOTE(nicksantos): When we remove the unknown type, the function types
// form a lattice with the universal constructor at the top of the lattice,
// and the LEAST_FUNCTION_TYPE type at the bottom of the lattice.
//
// When we introduce the unknown type, it's much more difficult to make
// heads or tails of the partial ordering of types, because there's no
// clear hierarchy between the different components (parameter types and
// return types) in the ArrowType.
//
// Rather than make the situation more complicated by introducing new
// types (like unions of functions), we just fallback on the simpler
// approach of getting things right at the top and the bottom of the
// lattice.
//
// If there are unknown parameters or return types making things
// ambiguous, then sup(A, B) is always the top function type, and
// inf(A, B) is always the bottom function type.
Preconditions.checkNotNull(that);
if (isEquivalentTo(that)) {
return this;
}
// If these are ordinary functions, then merge them.
// Don't do this if any of the params/return
// values are unknown, because then there will be cycles in
// their local lattice and they will merge in weird ways.
if (isOrdinaryFunction() && that.isOrdinaryFunction() &&
!this.call.hasUnknownParamsOrReturn() &&
!that.call.hasUnknownParamsOrReturn()) {
// Check for the degenerate case, but double check
// that there's not a cycle.
boolean isSubtypeOfThat = isSubtype(that);
boolean isSubtypeOfThis = that.isSubtype(this);
if (isSubtypeOfThat && !isSubtypeOfThis) {
return leastSuper ? that : this;
} else if (isSubtypeOfThis && !isSubtypeOfThat) {
return leastSuper ? this : that;
}
// Merge the two functions component-wise.
FunctionType merged = tryMergeFunctionPiecewise(that,
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> ObjectType foundType = null;
if (type.hasProperty(propertyName)) {
foundType = type;
}
for (ObjectType interfaceType : type.getCtorExtendedInterfaces()) {
if (interfaceType.hasProperty(propertyName)) {
foundType = getTopDefiningInterface(interfaceType, propertyName);
}
}
return foundType;
}
/**
* Given a constructor or an interface type and a property, finds the
* top-most superclass that has the property defined (including this
* constructor).
*/
public ObjectType getTopMostDefiningType(String propertyName) {
Preconditions.checkState(isConstructor() || isInterface());
Preconditions.checkArgument(getInstanceType().hasProperty(propertyName));
FunctionType ctor = this;
if (isInterface()) {
return getTopDefiningInterface(getInstanceType(), propertyName);
}
ObjectType topInstanceType = null;
do {
topInstanceType = ctor.getInstanceType();
ctor = ctor.getSuperClassConstructor();
} while (ctor != null
&& ctor.getPrototype().hasProperty(propertyName));
return topInstanceType;
}
/**
* Two function types are equal if their signatures match. Since they don't
* have signatures, two interfaces are equal if their names match.
*/
boolean checkFunctionEquivalenceHelper(
FunctionType that, EquivalenceMethod eqMethod) {
if (isConstructor()) {
if (that.isConstructor()) {
return this == that;
}
return false;
}
if (isInterface()) {
if (that.isInterface()) {
return getReferenceName().equals(that.getReferenceName());
}
return false;
}
if (that.isInterface()) {
return false;
}
return typeOfThis.checkEquivalenceHelper(that.typeOfThis, eqMethod) &&
call.checkArrowEquivalenceHelper(that.call, eqMethod);
}
@Override
public int hashCode() {
return isInterface() ? getReferenceName().hashCode() : call.hashCode();
}
public boolean hasEqualCallType(FunctionType otherType) {
return this.call.checkArrowEquivalenceHelper(
otherType.call, EquivalenceMethod.IDENTITY);
}
/**
* Informally, a function is represented by
* {@code function (params): returnType} where the {@code params} is a comma
* separated list of types, the first one being a special
* {@code this:T} if the function expects a known type for {@code this}.
*/
@Override
String toStringHelper(boolean forAnnotations) {
if (!isPrettyPrint() ||
this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) {
return "Function";
}
setPrettyPrint(false);
StringBuilder b = new StringBuilder(32);
b.append("function (");
int paramNum = call.parameters.getChildCount();
boolean hasKnownTypeOfThis = !(typeOfThis instanceof UnknownType);
if (hasKnownTypeOfThis) {
if (isConstructor()) {
b.append("new:");
} else {
b.append("this:");
}
b.append(typeOfThis.toStringHelper(forAnnotations));
}
if (paramNum > 0) {
if (hasKnownTypeOfThis) {
b.append(", ");
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>
}
Node p = call.parameters.getFirstChild();
appendArgString(b, p, forAnnotations);
p = p.getNext();
while (p != null) {
b.append(", ");
appendArgString(b, p, forAnnotations);
p = p.getNext();
}
}
b.append("): ");
b.append(call.returnType.toStringHelper(forAnnotations));
setPrettyPrint(true);
return b.toString();
}
private void appendArgString(
StringBuilder b, Node p, boolean forAnnotations) {
if (p.isVarArgs()) {
appendVarArgsString(b, p.getJSType(), forAnnotations);
} else if (p.isOptionalArg()) {
appendOptionalArgString(b, p.getJSType(), forAnnotations);
} else {
b.append(p.getJSType().toStringHelper(forAnnotations));
}
}
/** Gets the string representation of a var args param. */
private void appendVarArgsString(StringBuilder builder, JSType paramType,
boolean forAnnotations) {
if (paramType.isUnionType()) {
// Remove the optionality from the var arg.
paramType = paramType.toMaybeUnionType().getRestrictedUnion(
registry.getNativeType(JSTypeNative.VOID_TYPE));
}
builder.append("...[").append(
paramType.toStringHelper(forAnnotations)).append("]");
}
/** Gets the string representation of an optional param. */
private void appendOptionalArgString(
StringBuilder builder, JSType paramType, boolean forAnnotations) {
if (paramType.isUnionType()) {
// Remove the optionality from the var arg.
paramType = paramType.toMaybeUnionType().getRestrictedUnion(
registry.getNativeType(JSTypeNative.VOID_TYPE));
}
builder.append(paramType.toStringHelper(forAnnotations)).append("=");
}
/**
* A function is a subtype of another if their call methods are related via
* subtyping and {@code this} is a subtype of {@code that} with regard to
* the prototype chain.
*/
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtypeHelper(this, that)) {
return true;
}
if (that.isFunctionType()) {
FunctionType other = that.toMaybeFunctionType();
if (other.isInterface()) {
// Any function can be assigned to an interface function.
return true;
}
if (isInterface()) {
// An interface function cannot be assigned to anything.
return false;
}
// If functionA is a subtype of functionB, then their "this" types
// should be contravariant. However, this causes problems because
// of the way we enforce overrides. Because function(this:SubFoo)
// is not a subtype of function(this:Foo), our override check treats
// this as an error. Let's punt on all this for now.
// TODO(nicksantos): fix this.
boolean treatThisTypesAsCovariant =
// An interface 'this'-type is non-restrictive.
// In practical terms, if C implements I, and I has a method m,
// then any m doesn't necessarily have
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> to C#m's 'this'
// type doesn't need to match I.
(other.typeOfThis.toObjectType() != null &&
other.typeOfThis.toObjectType().getConstructor() != null &&
other.typeOfThis.toObjectType().getConstructor().isInterface()) ||
// If one of the 'this' types is covariant of the other,
// then we'll treat them as covariant (see comment above).
other.typeOfThis.isSubtype(this.typeOfThis) ||
this.typeOfThis.isSubtype(other.typeOfThis);
return treatThisTypesAsCovariant && this.call.isSubtype(other.call);
}
return getNativeType(JSTypeNative.FUNCTION_PROTOTYPE).isSubtype(that);
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseFunctionType(this);
}
@Override <T> T visit(RelationshipVisitor<T> visitor, JSType that) {
return visitor.caseFunctionType(this, that);
}
/**
* Gets the type of instance of this function.
* @throws IllegalStateException if this function is not a constructor
* (see {@link #isConstructor()}).
*/
public ObjectType getInstanceType() {
Preconditions.checkState(hasInstanceType());
return typeOfThis.toObjectType();
}
/**
* Sets the instance type. This should only be used for special
* native types.
*/
void setInstanceType(ObjectType instanceType) {
typeOfThis = instanceType;
}
/**
* Returns whether this function type has an instance type.
*/
public boolean hasInstanceType() {
return isConstructor() || isInterface();
}
/**
* Gets the type of {@code this} in this function.
*/
@Override
public JSType getTypeOfThis() {
return typeOfThis.isEmptyType() ?
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE) : typeOfThis;
}
/**
* Gets the source node or null if this is an unknown function.
*/
public Node getSource() {
return source;
}
/**
* Sets the source node.
*/
public void setSource(Node source) {
if (prototypeSlot != null) {
// NOTE(bashir): On one hand when source is null we want to drop any
// references to old nodes retained in prototypeSlot. On the other hand
// we cannot simply drop prototypeSlot, so we retain all information
// except the propertyNode for which we use an approximation! These
// details mostly matter in hot-swap passes.
if (source == null || prototypeSlot.getNode() == null) {
prototypeSlot = new Property(prototypeSlot.getName(),
prototypeSlot.getType(), prototypeSlot.isTypeInferred(), source);
}
}
this.source = source;
}
/** Adds a type to the list of subtypes for this type. */
private void addSubType(FunctionType subType) {
if (subTypes == null) {
subTypes = Lists.newArrayList();
}
subTypes.add(subType);
}
@Override
public void clearCachedValues() {
super.clearCachedValues();
if (subTypes != null) {
for (FunctionType subType : subTypes)
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS> {
subType.clearCachedValues();
}
}
if (!isNativeObjectType()) {
if (hasInstanceType()) {
getInstanceType().clearCachedValues();
}
if (prototypeSlot != null) {
((ObjectType) prototypeSlot.getType()).clearCachedValues();
}
}
}
/**
* Returns a list of types that are subtypes of this type. This is only valid
* for constructor functions, and may be null. This allows a downward
* traversal of the subtype graph.
*/
public List<FunctionType> getSubTypes() {
return subTypes;
}
@Override
public boolean hasCachedValues() {
return prototypeSlot != null || super.hasCachedValues();
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setResolvedTypeInternal(this);
call = (ArrowType) safeResolve(call, t, scope);
if (prototypeSlot != null) {
prototypeSlot.setType(
safeResolve(prototypeSlot.getType(), t, scope));
}
// Warning about typeOfThis if it doesn't resolve to an ObjectType
// is handled further upstream.
//
// TODO(nicksantos): Handle this correctly if we have a UnionType.
//
// TODO(nicksantos): In ES3, the run-time coerces "null" to the global
// activation object. In ES5, it leaves it as null. Just punt on this
// issue for now by coercing out null. This is complicated by the
// fact that when most people write @this {Foo}, they really don't
// mean "nullable Foo". For certain tags (like @extends) we de-nullify
// the name for them.
JSType maybeTypeOfThis = safeResolve(typeOfThis, t, scope);
if (maybeTypeOfThis != null) {
maybeTypeOfThis = maybeTypeOfThis.restrictByNotNullOrUndefined();
}
if (maybeTypeOfThis instanceof ObjectType) {
typeOfThis = maybeTypeOfThis;
}
boolean changed = false;
ImmutableList.Builder<ObjectType> resolvedInterfaces =
ImmutableList.builder();
for (ObjectType iface : implementedInterfaces) {
ObjectType resolvedIface = (ObjectType) iface.resolve(t, scope);
resolvedInterfaces.add(resolvedIface);
changed |= (resolvedIface != iface);
}
if (changed) {
implementedInterfaces = resolvedInterfaces.build();
}
if (subTypes != null) {
for (int i = 0; i < subTypes.size(); i++) {
subTypes.set(
i, JSType.toMaybeFunctionType(subTypes.get(i).resolve(t, scope)));
}
}
return super.resolveInternal(t, scope);
}
@Override
public String toDebugHashCodeString() {
if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) {
return super.toDebugHashCodeString();
}
StringBuilder b = new StringBuilder(32);
b.append("function (");
int paramNum = call.parameters.getChildCount();
boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType();
if (hasKnownTypeOfThis) {
b.append("this:");
b.append(getDebugHashCodeStringOf
Closure, 112
<FILEB>
<CHANGES>
Map<TemplateType, JSType> inferred = Maps.filterKeys(
inferTemplateTypesFromParameters(fnType, n),
new Predicate<TemplateType>() {
@Override
public boolean apply(TemplateType key) {
return keys.contains(key);
}}
);
<CHANGEE>
<FILEE>
<FILEB>
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
final ImmutableList<TemplateType> keys = fnType.getTemplateTypeMap()
.getTemplateKeys();
if (keys.isEmpty()) {
return false;
}
// Try to infer the template types
<CHANGES>
Map<TemplateType, JSType> inferred =
inferTemplateTypesFromParameters(fnType, n);
<CHANGEE>
// Replace all template types. If we couldn't find a replacement, we
// replace it with UNKNOWN.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return replacer.madeChanges;
}
<FILEE>
<SCANS>(typeOfThis));
}
if (paramNum > 0) {
if (hasKnownTypeOfThis) {
b.append(", ");
}
Node p = call.parameters.getFirstChild();
b.append(getDebugHashCodeStringOf(p.getJSType()));
p = p.getNext();
while (p != null) {
b.append(", ");
b.append(getDebugHashCodeStringOf(p.getJSType()));
p = p.getNext();
}
}
b.append(")");
b.append(": ");
b.append(getDebugHashCodeStringOf(call.returnType));
return b.toString();
}
private String getDebugHashCodeStringOf(JSType type) {
if (type == this) {
return "me";
} else {
return type.toDebugHashCodeString();
}
}
/** Create a new constructor with the parameters and return type stripped. */
public FunctionType cloneWithoutArrowType() {
FunctionType result = new FunctionType(
registry, getReferenceName(), source,
registry.createArrowType(null, null), getInstanceType(),
null, true, false);
result.setPrototypeBasedOn(getInstanceType());
return result;
}
@Override
public boolean hasAnyTemplateTypesInternal() {
return getTemplateTypeMap().numUnfilledTemplateKeys() > 0
|| typeOfThis.hasAnyTemplateTypes()
|| call.hasAnyTemplateTypes();
}
}